import { NextResponse } from 'next/server';
import { authSession } from '@/lib/auth/session-server';
import { query } from '@/lib/db-postgres';
import {
  deleteService,
  deleteApplication,
  deleteProject as deleteCoolifyProject,
} from '@/lib/coolify';
import {
  ensureProjectResourcesTable,
  getProjectResourceUuids,
} from '@/lib/projects';
import { deleteRepo } from '@/lib/gitea';

/**
 * POST /api/projects/delete
 *
 * Tears down a Vibn project end-to-end.
 *
 * Old behavior (broken): only deleted the fs_projects row. The dev
 * container, Coolify project, and project_resources links all kept
 * existing — counting against quota and showing up in API listings as
 * orphans. This caused users to hit the 3-active-dev-container cap
 * with ghosts from projects they thought they had deleted.
 *
 * New behavior (this file): cascade.
 *
 *   1. Stop and delete the dev container Coolify service.
 *   2. Delete every linked Coolify resource (apps + services + DBs the
 *      AI created under this project).
 *   3. Delete the per-project Coolify project shell (if it ends up
 *      empty after step 2 — best effort, ignore failure).
 *   4. Delete the Gitea repo backing this project (best effort).
 *   5. Unlink fs_sessions from the project.
 *   6. Delete the fs_projects row.
 *
 * Note: fs_project_dev_containers and fs_project_resources rows are
 * cleared during steps 1–2 as part of container/resource teardown.
 *
 * What we DO NOT delete:
 *   - The Sentry project — error history is independent evidence.
 *
 * Failures inside the cascade are logged but don't abort the whole
 * delete. Half-deleted projects are already broken; finishing the
 * delete and leaving an orphan Coolify resource for manual cleanup
 * is strictly better than rolling back to a half-state.
 */

/** Resolve `owner/repo` from project JSON (`giteaRepo` or parsed URL). */
function resolveGiteaOwnerRepo(data: Record<string, unknown>): {
  owner: string;
  repo: string;
} | null {
  const full = typeof data.giteaRepo === 'string' ? data.giteaRepo.trim() : '';
  if (full) {
    const idx = full.indexOf('/');
    if (idx > 0 && idx < full.length - 1) {
      return { owner: full.slice(0, idx), repo: full.slice(idx + 1) };
    }
  }
  const urlStr =
    typeof data.giteaRepoUrl === 'string' ? data.giteaRepoUrl.trim() : '';
  if (urlStr) {
    try {
      const u = new URL(urlStr);
      const segs = u.pathname
        .replace(/^\/+|\/+$/g, '')
        .split('/')
        .filter(Boolean);
      if (segs.length >= 2) {
        const repoName = segs[segs.length - 1]!.replace(/\.git$/i, '');
        const ownerName = segs[segs.length - 2]!;
        if (ownerName && repoName) return { owner: ownerName, repo: repoName };
      }
    } catch {
      /* ignore invalid URL */
    }
  }
  return null;
}

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

    const { projectId } = await request.json();
    if (!projectId) {
      return NextResponse.json({ error: 'Project ID is required' }, { status: 400 });
    }

    // Verify ownership before we destroy anything.
    const rows = await query<{ id: string; data: any; workspace: string | null }>(`
      SELECT p.id, p.data, p.workspace
      FROM fs_projects p
      JOIN fs_users u ON u.id = p.user_id
      WHERE p.id = $1 AND u.data->>'email' = $2
      LIMIT 1
    `, [projectId, session.user.email]);

    if (rows.length === 0) {
      return NextResponse.json({ error: 'Project not found or unauthorized' }, { status: 404 });
    }

    const project = rows[0];
    const data = project.data || {};
    const coolifyProjectUuid: string | null = data.coolifyProjectUuid ?? null;
    const giteaRepoUrl: string | null = data.giteaRepoUrl ?? null;

    const teardown = {
      devContainerDeleted: false as boolean | string,
      resourcesDeleted: 0,
      resourcesFailed: 0,
      coolifyProjectDeleted: false as boolean | string,
      giteaRepoDeleted: false as boolean | string,
      sessionsUnlinked: 0,
      giteaRepoUrl,
    };

    // 1. Dev container — fetch row, then call Coolify delete-service
    //    with deleteVolumes=true and dockerCleanup=true so the
    //    /workspace volume goes away with the container. Otherwise
    //    abandoned volumes pile up on the host.
    try {
      const dcRows = await query<{ service_uuid: string }>(
        `SELECT service_uuid FROM fs_project_dev_containers WHERE project_id = $1`,
        [projectId],
      );
      if (dcRows[0]?.service_uuid) {
        try {
          await deleteService(dcRows[0].service_uuid, {
            deleteVolumes: true,
            dockerCleanup: true,
            deleteConnectedNetworks: true,
          });
          teardown.devContainerDeleted = true;
        } catch (err) {
          teardown.devContainerDeleted =
            'coolify_delete_failed:' +
            (err instanceof Error ? err.message.slice(0, 120) : 'unknown');
        }
        await query(
          `DELETE FROM fs_project_dev_containers WHERE project_id = $1`,
          [projectId],
        );
      }
    } catch (err) {
      console.warn(`[delete-project ${projectId}] dev container teardown failed`, err);
    }

    // 2. Linked Coolify resources (apps, services, databases the AI
    //    created via apps_create / services_create / databases_create).
    try {
      await ensureProjectResourcesTable();
      const linked = await getProjectResourceUuids(projectId);
      for (const [uuid, type] of linked.entries()) {
        try {
          if (type === 'application') {
            await deleteApplication(uuid, {
              deleteConfigurations: true,
              deleteVolumes: true,
              dockerCleanup: true,
              deleteConnectedNetworks: true,
            });
          } else if (type === 'service') {
            await deleteService(uuid, {
              deleteConfigurations: true,
              deleteVolumes: true,
              dockerCleanup: true,
              deleteConnectedNetworks: true,
            });
          }
          // database type intentionally not auto-deleted yet — Coolify
          // databases hold user data and we don't have a reliable signal
          // that the user intended to throw it away. Surface them in
          // the response so the user (or AI) can clean up manually.
          teardown.resourcesDeleted++;
        } catch (err) {
          teardown.resourcesFailed++;
          console.warn(
            `[delete-project ${projectId}] failed to delete ${type} ${uuid}`,
            err,
          );
        }
      }
      await query(
        `DELETE FROM fs_project_resources WHERE project_id = $1`,
        [projectId],
      );
    } catch (err) {
      console.warn(`[delete-project ${projectId}] resource cleanup failed`, err);
    }

    // 3. Per-project Coolify project shell. Only delete it if it's
    //    a per-project shell (one project_uuid per Vibn project,
    //    populated since Path B week 2). Legacy `vibn-ws-*` shared
    //    projects are NOT deleted because other Vibn projects still
    //    live in them.
    if (coolifyProjectUuid) {
      try {
        // Only delete if no other Vibn project still references it.
        const sharers = await query<{ count: number }>(
          `SELECT COUNT(*)::int AS count FROM fs_projects
             WHERE id <> $1 AND data->>'coolifyProjectUuid' = $2`,
          [projectId, coolifyProjectUuid],
        );
        if ((sharers[0]?.count ?? 0) === 0) {
          try {
            await deleteCoolifyProject(coolifyProjectUuid);
            teardown.coolifyProjectDeleted = true;
          } catch (err) {
            teardown.coolifyProjectDeleted =
              'coolify_delete_failed:' +
              (err instanceof Error ? err.message.slice(0, 120) : 'unknown');
          }
        } else {
          teardown.coolifyProjectDeleted = 'skipped_shared';
        }
      } catch (err) {
        console.warn(`[delete-project ${projectId}] coolify project teardown failed`, err);
      }
    }

    // 4. Gitea repo — remove canonical repo so slug/repo name can be reused.
    const giteaTarget = resolveGiteaOwnerRepo(data as Record<string, unknown>);
    if (giteaTarget) {
      try {
        await deleteRepo(giteaTarget.owner, giteaTarget.repo);
        teardown.giteaRepoDeleted = true;
      } catch (err) {
        teardown.giteaRepoDeleted =
          'gitea_delete_failed:' +
          (err instanceof Error ? err.message.slice(0, 200) : 'unknown');
        console.warn(
          `[delete-project ${projectId}] Gitea delete failed for ${giteaTarget.owner}/${giteaTarget.repo}`,
          err,
        );
      }
    }

    // 5. Unlink sessions (preserve their content — they may reference
    //    learnings the user wants under a different project later).
    const sessionResult = await query<{ count: number }>(
      `SELECT COUNT(*)::int AS count FROM fs_sessions WHERE data->>'projectId' = $1`,
      [projectId],
    );
    teardown.sessionsUnlinked = sessionResult[0]?.count ?? 0;
    await query(
      `UPDATE fs_sessions
         SET data = jsonb_set(
           jsonb_set(data, '{projectId}', 'null'),
           '{needsProjectAssociation}', 'true'
         )
       WHERE data->>'projectId' = $1`,
      [projectId],
    );

    // 6. Drop the project row last so a partial cascade above
    //    doesn't leave the project record in a misleading state.
    await query(`DELETE FROM fs_projects WHERE id = $1`, [projectId]);

    return NextResponse.json({
      success: true,
      message: 'Project deleted.',
      teardown,
    });
  } catch (error) {
    console.error('[POST /api/projects/delete] Error:', error);
    return NextResponse.json(
      {
        error: 'Failed to delete project',
        details: error instanceof Error ? error.message : String(error),
      },
      { status: 500 },
    );
  }
}
