import {
  preparePublicGitSource,
  type ProjectImportOutcome,
  type ProjectView,
  type PublicGitImportResult,
  type PreparedPublicGitSource,
} from "@code/primitives/project";
import { v } from "convex/values";
import { Effect } from "effect";

import type { Id } from "./_generated/dataModel";
import { action, internalMutation, query } from "./_generated/server";
import { createInitialArtifacts } from "./artifactModel";
import { requireCurrentOrganization } from "./authz";
import {
  makeProjectMutationStore,
  makeProjectQueryStore,
  persistPublicGitImportTransaction,
} from "./projectStore";

// ---------------------------------------------------------------------------
// Internal: persist a public Git import in one transaction
// ---------------------------------------------------------------------------

export const persistPublicGitImport = internalMutation({
  args: {
    userId: v.string(),
    source: v.object({
      host: v.string(),
      projectName: v.string(),
      repositoryPath: v.string(),
      normalizedUrl: v.string(),
      url: v.string(),
    }),
    remote: v.object({
      defaultBranch: v.optional(v.string()),
      documents: v.array(
        v.object({
          kind: v.union(
            v.literal("readme"),
            v.literal("agents"),
            v.literal("product"),
            v.literal("business"),
            v.literal("design"),
            v.literal("tech")
          ),
          path: v.string(),
          content: v.string(),
        })
      ),
      warnings: v.array(
        v.object({
          path: v.string(),
          message: v.string(),
        })
      ),
    }),
  },
  handler: async (ctx, args): Promise<ProjectImportOutcome> => {
    const result = await persistPublicGitImportTransaction(
      ctx,
      args.userId,
      args.source as PreparedPublicGitSource,
      args.remote as PublicGitImportResult
    );

    // Seed operational artifacts on first creation (detected by checking
    // existing artifacts)
    const existingArtifacts = await ctx.db
      .query("projectArtifacts")
      .withIndex("by_project", (q) =>
        q.eq("projectId", result.id as unknown as Id<"projects">)
      )
      .first();
    if (!existingArtifacts) {
      const now = Date.now();
      const artifacts = createInitialArtifacts();
      await Promise.all(
        artifacts.map(({ content, path }) =>
          ctx.db.insert("projectArtifacts", {
            content,
            createdAt: now,
            path,
            projectId: result.id as unknown as Id<"projects">,
            revision: 1,
            updatedAt: now,
          })
        )
      );
      await ctx.db.insert("projectEvents", {
        createdAt: now,
        data: { host: result.source.host, url: result.source.url },
        kind: "project.connected",
        projectId: result.id as unknown as Id<"projects">,
      });
    }

    return result;
  },
});

// ---------------------------------------------------------------------------
// Internal: put a context document
// ---------------------------------------------------------------------------

export const putContextDocument = internalMutation({
  args: {
    userId: v.string(),
    projectId: v.id("projects"),
    write: v.union(
      v.object({
        _tag: v.literal("Repository"),
        content: v.string(),
        kind: v.union(
          v.literal("readme"),
          v.literal("agents"),
          v.literal("product"),
          v.literal("business"),
          v.literal("design"),
          v.literal("tech")
        ),
        sourceUrl: v.string(),
      }),
      v.object({
        _tag: v.literal("PublicTextUrl"),
        content: v.string(),
        kind: v.union(
          v.literal("readme"),
          v.literal("agents"),
          v.literal("product"),
          v.literal("business"),
          v.literal("design"),
          v.literal("tech")
        ),
        sourceUrl: v.string(),
      }),
      v.object({
        _tag: v.literal("UserText"),
        content: v.string(),
        kind: v.union(
          v.literal("readme"),
          v.literal("agents"),
          v.literal("product"),
          v.literal("business"),
          v.literal("design"),
          v.literal("tech")
        ),
        origin: v.union(v.literal("paste"), v.literal("upload")),
      })
    ),
  },
  handler: async (ctx, args) => {
    const store = makeProjectMutationStore(ctx);
    return store.putContext(args.userId, args.projectId, args.write as never);
  },
});

// ---------------------------------------------------------------------------
// Public query: list projects
// ---------------------------------------------------------------------------

export const list = query({
  args: {},
  handler: async (ctx): Promise<ProjectView[]> => {
    const { userId } = await requireCurrentOrganization(ctx);
    const store = makeProjectQueryStore(ctx);
    return store.listProjects(userId);
  },
});

// ---------------------------------------------------------------------------
// Public query: get a project
// ---------------------------------------------------------------------------

export const get = query({
  args: { projectId: v.id("projects") },
  handler: async (ctx, args): Promise<ProjectView | null> => {
    const { userId } = await requireCurrentOrganization(ctx);
    const store = makeProjectQueryStore(ctx);
    return store.getProject(userId, args.projectId);
  },
});

// ---------------------------------------------------------------------------
// Public action: import a public Git repository
// (daemon wiring added in the Daemon phase)
// ---------------------------------------------------------------------------

export const importPublicGit = action({
  args: { repositoryUrl: v.string() },
  handler: async (_ctx, args): Promise<ProjectImportOutcome> => {
    // Normalize through the domain layer to validate the URL before the
    // daemon adapter is wired.
    await Effect.runPromise(preparePublicGitSource(args.repositoryUrl));
    // The daemon PublicGit adapter is wired in the Daemon phase.
    throw new Error("PublicGit import is not yet wired to the daemon adapter");
  },
});

// ---------------------------------------------------------------------------
// Public mutation: put context text (paste/upload)
// ---------------------------------------------------------------------------

export const putText = internalMutation({
  args: {
    projectId: v.id("projects"),
    kind: v.union(
      v.literal("readme"),
      v.literal("agents"),
      v.literal("product"),
      v.literal("business"),
      v.literal("design"),
      v.literal("tech")
    ),
    content: v.string(),
    origin: v.union(v.literal("paste"), v.literal("upload")),
  },
  handler: async (ctx, args) => {
    const { userId } = await requireCurrentOrganization(ctx);
    const store = makeProjectMutationStore(ctx);
    return store.putContext(userId, args.projectId, {
      _tag: "UserText" as const,
      content: args.content,
      kind: args.kind,
      origin: args.origin,
    });
  },
});
