import { NextResponse } from 'next/server';
import { authSession } from "@/lib/auth/session-server";
import { query } from '@/lib/db-postgres';
import { randomUUID } from 'crypto';
import { createRepo, createWebhook, getRepo, listWebhooks, GITEA_ADMIN_USER_EXPORT } from '@/lib/gitea';
import { getOrCreateProvisionedWorkspace } from '@/lib/workspaces';
import { ensureProjectCoolifyProject } from '@/lib/projects';
import { ensureSentryProject } from '@/lib/integrations/sentry';
import { assertProjectQuota, QuotaExceededError } from '@/lib/quotas';
import { loadGithubIntegration } from '@/lib/integrations/github';
import type { ProjectPhaseData, ProjectPhaseScores } from '@/lib/types/project-artifacts';
import { getStarterKit } from '@/lib/design-kits/registry';
import type { DesignKitPersisted } from '@/lib/design-kits/types';
import { DEFAULT_DESIGN_KIT_ID } from '@/lib/design-kits/types';
import { normalizeSeedDocument } from '@/lib/server/parse-seed-document';

const GITEA_ADMIN_USER = GITEA_ADMIN_USER_EXPORT;
const APP_URL = process.env.NEXTAUTH_URL ?? process.env.NEXT_PUBLIC_APP_URL ?? 'https://vibnai.com';
const GITEA_WEBHOOK_SECRET = process.env.GITEA_WEBHOOK_SECRET ?? 'vibn-webhook-secret';

export async function POST(request: Request) {
  try {
    const session = await authSession();
    if (!session?.user?.email) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    }

    const email = session.user.email;
    const workspace = email.split('@')[0].toLowerCase().replace(/[^a-z0-9]+/g, '-') + '-account';

    // Upsert user into fs_users — guarantees the FK target exists
    const userData = JSON.stringify({
      email,
      name: session.user.name,
      image: session.user.image,
      workspace,
    });
    const existingUser = await query<{ id: string; data: any }>(
      `SELECT id, data FROM fs_users WHERE data->>'email' = $1 LIMIT 1`,
      [email]
    );
    if (existingUser.length === 0) {
      await query(
        `INSERT INTO fs_users (id, user_id, data) VALUES (gen_random_uuid()::text, $1, $2::jsonb)`,
        [session.user.id, userData]
      );
    } else {
      await query(
        `UPDATE fs_users SET user_id = $1, data = data || $2::jsonb, updated_at = NOW() WHERE id = $3`,
        [session.user.id, userData, existingUser[0].id]
      );
    }

    // Fetch the canonical fs_users row (now guaranteed to exist)
    const users = await query<{ id: string; data: any }>(`
      SELECT id, data FROM fs_users WHERE data->>'email' = $1 LIMIT 1
    `, [email]);

    const firebaseUserId = users[0]!.id;

    // Resolve (and lazily provision) the user's workspace. Provides:
    //   - vibnWorkspace.coolify_project_uuid → namespace for Coolify apps/DBs
    //   - vibnWorkspace.gitea_org            → owner for Gitea repos
    // If provisioning failed for either, we fall back to legacy admin
    // identifiers so the project create still succeeds (with degraded isolation).
    let vibnWorkspace = await getOrCreateProvisionedWorkspace({
      userId: firebaseUserId,
      email,
      displayName: session.user.name ?? null,
    });

    const repoOwner = vibnWorkspace.gitea_org ?? GITEA_ADMIN_USER;

    const body = await request.json();
    const {
      projectName,
      projectType,
      slug,
      vision,
      product,
      workspacePath,
      chatgptUrl,
      githubRepo,
      githubRepoId,
      githubRepoUrl,
      githubDefaultBranch,
      githubToken,
      creationMode,
      sourceData,
      audience,
      designKitId: rawDesignKitId,
      seedDocument: seedDocumentRaw,
    } = body;

    const requestedKit =
      typeof rawDesignKitId === 'string' ? rawDesignKitId.trim() : '';
    const resolvedDesignKitId = getStarterKit(requestedKit)
      ? requestedKit
      : DEFAULT_DESIGN_KIT_ID;
    const designKit: DesignKitPersisted = {
      kitId: resolvedDesignKitId,
      perKit: {},
    };

    let seedDocumentPersisted: Awaited<ReturnType<typeof normalizeSeedDocument>> = null;
    try {
      seedDocumentPersisted = await normalizeSeedDocument(seedDocumentRaw);
    } catch (seedErr) {
      const msg = seedErr instanceof Error ? seedErr.message : String(seedErr);
      return NextResponse.json({ error: msg }, { status: 400 });
    }

    const mergedSource: Record<string, unknown> =
      sourceData != null && typeof sourceData === 'object' && !Array.isArray(sourceData)
        ? { ...(sourceData as Record<string, unknown>) }
        : {};
    delete mergedSource.seedDocument;

    // Check slug uniqueness
    const existing = await query(`SELECT id FROM fs_projects WHERE slug = $1 LIMIT 1`, [slug]);
    if (existing.length > 0) {
      return NextResponse.json({ error: 'Project slug already exists' }, { status: 400 });
    }

    // Per-workspace project quota — soft cap at VIBN_QUOTA_MAX_PROJECTS_PER_WORKSPACE
    // (default 3) to bound runaway resource cost from bad-actor signups.
    try {
      await assertProjectQuota({ id: vibnWorkspace.id, slug: workspace });
    } catch (qe) {
      if (qe instanceof QuotaExceededError) {
        return NextResponse.json(
          { error: qe.message, code: qe.code, current: qe.current, limit: qe.limit },
          { status: 402 }, // 402 Payment Required — semantically "you've hit a tier limit".
        );
      }
      throw qe;
    }

    const projectId = randomUUID();
    const now = new Date().toISOString();

    // ──────────────────────────────────────────────
    // 1. Provision Gitea repo
    // ──────────────────────────────────────────────
    let giteaRepo: string | null = null;
    let giteaRepoUrl: string | null = null;
    let giteaCloneUrl: string | null = null;
    let giteaSshUrl: string | null = null;
    let giteaWebhookId: number | null = null;
    let giteaError: string | null = null;

    try {
      const repoName = slug; // e.g. "taskmaster"
      let repo;

      try {
        repo = await createRepo(repoName, {
          description: `${projectName} — managed by Vibn`,
          private: true,
          auto_init: false,
          owner: repoOwner,
        });
        console.log(`[API] Gitea repo created: ${repoOwner}/${repoName}`);
      } catch (createErr) {
        const msg = createErr instanceof Error ? createErr.message : String(createErr);
        // 409 = repo already exists — link to it instead of failing
        if (msg.includes('409')) {
          console.log(`[API] Gitea repo already exists, linking to ${repoOwner}/${repoName}`);
          repo = await getRepo(repoOwner, repoName);
          if (!repo) throw new Error(`Repo ${repoName} exists but could not be fetched`);
        } else {
          throw createErr;
        }
      }

      giteaRepo = repo.full_name;          // e.g. "mark/taskmaster"
      giteaRepoUrl = repo.html_url;        // e.g. "https://git.vibnai.com/mark/taskmaster"
      giteaCloneUrl = repo.clone_url;
      giteaSshUrl = repo.ssh_url;

      // If a GitHub repo was provided, mirror it as-is.
      // Otherwise leave the repo empty — the user (or AI) decides what to
      // put in it. The old turborepo auto-scaffold is no longer pushed
      // because most projects don't need a 4-app monorepo and the AI
      // can scaffold whatever the user actually wants on demand.
      if (githubRepoUrl) {
        // Prefer an explicitly-passed token; otherwise fall back to the
        // OAuth-linked token on this user's account so private mirrors
        // work without the user pasting a PAT.
        let effectiveToken = githubToken as string | undefined;
        if (!effectiveToken) {
          const link = await loadGithubIntegration(email);
          if (link) effectiveToken = link.token;
        }
        const agentRunnerUrl = process.env.AGENT_RUNNER_URL ?? 'http://localhost:3333';
        const mirrorRes = await fetch(`${agentRunnerUrl}/api/mirror`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            github_url: githubRepoUrl,
            gitea_repo: `${repoOwner}/${repoName}`,
            project_name: projectName,
            github_token: effectiveToken || undefined,
          }),
        });
        if (!mirrorRes.ok) {
          const detail = await mirrorRes.text();
          throw new Error(`GitHub mirror failed: ${detail}`);
        }
        console.log(`[API] GitHub repo mirrored to ${giteaRepo}`);
      }

      // Register webhook — skip if one already points to this project
      const webhookUrl = `${APP_URL}/api/webhooks/gitea?projectId=${projectId}`;
      const existingHooks = await listWebhooks(repoOwner, repoName).catch(() => []);
      const alreadyHooked = existingHooks.some(h => h.config.url.includes(projectId));

      if (!alreadyHooked) {
        const hook = await createWebhook(repoOwner, repoName, webhookUrl, GITEA_WEBHOOK_SECRET);
        giteaWebhookId = hook.id;
        console.log(`[API] Webhook registered: ${giteaRepo}, id: ${giteaWebhookId}`);
      } else {
        giteaWebhookId = existingHooks.find(h => h.config.url.includes(projectId))?.id ?? null;
        console.log(`[API] Webhook already exists for ${giteaRepo}`);
      }
    } catch (err) {
      // Non-fatal — log and continue. Project is still created.
      giteaError = err instanceof Error ? err.message : String(err);
      console.error('[API] Gitea provisioning failed (non-fatal):', giteaError);
    }

    // ──────────────────────────────────────────────
    // 2. Provision a dedicated Coolify project for this Vibn project
    // ──────────────────────────────────────────────
    // Each Vibn project gets its OWN Coolify project named
    // `vibn-{workspace-slug}-{project-slug}`. All apps/databases/services
    // the user (or AI) deploys for this project will land inside it.
    //
    // We don't pre-create any services anymore — the project starts empty
    // and the user/AI decides what to deploy via apps_create on demand.
    //
    // Note: ensureProjectCoolifyProject reads the fs_projects row, so we
    // insert a minimal row first, then provision Coolify, then update the
    // row with the full project data further below.
    await query(
      `INSERT INTO fs_projects (id, data, user_id, workspace, slug, vibn_workspace_id)
       VALUES ($1, '{}'::jsonb, $2, $3, $4, $5)
       ON CONFLICT (id) DO NOTHING`,
      [projectId, firebaseUserId, workspace, slug, vibnWorkspace.id],
    );
    const coolifyProjectUuid: string | null = await ensureProjectCoolifyProject(
      projectId,
      vibnWorkspace,
      { projectSlug: slug, projectName },
    );

    // Sentry-as-product: provision a Sentry project under the
    // shared `vibnai` org so any Coolify app deployed for this
    // Vibn project has a DSN waiting in env vars on first build.
    // Soft-fails — project create still succeeds without Sentry,
    // and apps.create will lazily retry the provisioning later.
    try {
      await ensureSentryProject({
        projectId,
        workspaceSlug: workspace,
        projectSlug: slug,
        projectName,
      });
    } catch (sentryErr) {
      console.warn('[API] Sentry provisioning failed (non-fatal):', sentryErr);
    }

    // ──────────────────────────────────────────────
    // 3. Save project record
    // ──────────────────────────────────────────────
    const projectData = {
      id: projectId,
      name: projectName,
      slug,
      userId: firebaseUserId,
      workspace,
      projectType,
      productName: product?.name || projectName,
      productVision: vision || '',
      // "team" = internal users (your team / employees) — SSO-style auth, no
      //          payments by default. "customers" = external public — sign-up
      //          + payments + transactional email by default. Drives which
      //          Infrastructure tiles get pre-staged.
      audience: audience === "team" ? "team" : "customers",
      hasLogo: product?.hasLogo || false,
      hasDomain: product?.hasDomain || false,
      hasWebsite: product?.hasWebsite || false,
      hasGithub: !!githubRepo,
      hasChatGPT: !!chatgptUrl,
      workspacePath: workspacePath || null,
      workspaceName: workspacePath ? workspacePath.split('/').pop() : null,
      githubRepo: githubRepo || null,
      githubRepoId: githubRepoId || null,
      githubRepoUrl: githubRepoUrl || null,
      githubDefaultBranch: githubDefaultBranch || null,
      chatgptUrl: chatgptUrl || null,
      extensionLinked: false,
      status: 'active',
      currentPhase: 'collector',
      phaseStatus: 'not_started',
      phaseData: {} as ProjectPhaseData,
      phaseScores: {} as ProjectPhaseScores,
      // Gitea provisioning
      giteaRepo,
      giteaRepoUrl,
      giteaCloneUrl,
      giteaSshUrl,
      giteaWebhookId,
      giteaError,
      // Context snapshot (kept fresh by webhooks)
      contextSnapshot: null,
      // Coolify project — one per VIBN project, scopes all app services + DBs.
      // Apps are deployed on-demand via apps_create (no auto-scaffold).
      coolifyProjectUuid,
      // How this project was created — drives the AI's first-chat seed.
      // Shape: { mode: "build"|"oss"|"import", sourceData: {...} } where
      // sourceData is the path-specific payload from the wizard.
      creationMode: creationMode ?? null,
      kickoff: creationMode ? {
        mode: creationMode,
        sourceData: {
          ...mergedSource,
          designKitId: resolvedDesignKitId,
          ...(seedDocumentPersisted ? { seedDocument: seedDocumentPersisted } : {}),
        },
        vision: vision || null,
        createdAt: now,
      } : null,
      designKit,
      // Import metadata
      isImport: !!githubRepoUrl,
      importAnalysisStatus: githubRepoUrl ? 'pending' : null,
      importAnalysisJobId: null as string | null,
      createdAt: now,
      updatedAt: now,
    };

    // Update the row we pre-inserted above with the full project data.
    // We merge with existing data so the coolifyProjectUuid set by
    // ensureProjectCoolifyProject() above is preserved.
    await query(`
      UPDATE fs_projects
         SET data = data || $2::jsonb,
             user_id = $3,
             workspace = $4,
             slug = $5,
             vibn_workspace_id = $6,
             updated_at = NOW()
       WHERE id = $1
    `, [projectId, JSON.stringify(projectData), firebaseUserId, workspace, slug, vibnWorkspace.id]);

    // Associate any unlinked sessions for this workspace path
    if (workspacePath) {
      await query(`
        UPDATE fs_sessions
        SET data = jsonb_set(
          jsonb_set(data, '{projectId}', $1::jsonb),
          '{needsProjectAssociation}', 'false'
        )
        WHERE user_id = $2
          AND data->>'workspacePath' = $3
          AND (data->>'needsProjectAssociation')::boolean = true
      `, [JSON.stringify(projectId), firebaseUserId, workspacePath]);
    }

    // ──────────────────────────────────────────────
    // 5. If this is an import, trigger the analysis agent
    // ──────────────────────────────────────────────
    let analysisJobId: string | null = null;
    if (githubRepoUrl && giteaRepo) {
      try {
        const agentRunnerUrl = process.env.AGENT_RUNNER_URL ?? 'http://localhost:3333';
        const jobRes = await fetch(`${agentRunnerUrl}/api/agent/run`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            agent: 'ImportAnalyzer',
            task: `Analyze this imported codebase (originally from ${githubRepoUrl}) and produce CODEBASE_MAP.md and MIGRATION_PLAN.md as described in your instructions.`,
            repo: giteaRepo,
          }),
        });
        if (jobRes.ok) {
          const jobData = await jobRes.json() as { jobId?: string };
          analysisJobId = jobData.jobId ?? null;
          // Store the job ID on the project record
          if (analysisJobId) {
            await query(
              `UPDATE fs_projects SET data = jsonb_set(jsonb_set(data, '{importAnalysisJobId}', $1::jsonb), '{importAnalysisStatus}', '"running"') WHERE id = $2`,
              [JSON.stringify(analysisJobId), projectId]
            );
          }
          console.log(`[API] Import analysis job started: ${analysisJobId}`);
        }
      } catch (analysisErr) {
        console.error('[API] Failed to start import analysis (non-fatal):', analysisErr);
      }
    }

    console.log('[API] Created project', projectId, slug, '| gitea:', giteaRepo ?? 'skipped', '| import:', !!githubRepoUrl);

    return NextResponse.json({
      success: true,
      projectId,
      slug,
      workspace,
      gitea: giteaRepo
        ? { repo: giteaRepo, repoUrl: giteaRepoUrl, cloneUrl: giteaCloneUrl, sshUrl: giteaSshUrl }
        : null,
      giteaError: giteaError ?? undefined,
      isImport: !!githubRepoUrl,
      analysisJobId: analysisJobId ?? undefined,
    });
  } catch (error) {
    console.error('[POST /api/projects/create] Error:', error);
    return NextResponse.json(
      { error: 'Failed to create project', details: error instanceof Error ? error.message : String(error) },
      { status: 500 }
    );
  }
}
