"use server";

import { revalidatePath } from "next/cache";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { AppwriteException, Query } from "node-appwrite";

import {
  BUCKETS,
  DATABASE_ID,
  TABLES,
  type JobFile,
  type TenantSettings,
} from "./schema";
import { APPWRITE_SESSION_COOKIE, createAdminClient } from "./server";
import { ACTIVE_TENANT_COOKIE } from "./tenant-types";
import { requireRole, requireTenant } from "./tenant-guard";

export type DeleteWorkspaceState = {
  ok: boolean;
  error?: string;
};

export const initialDeleteWorkspaceState: DeleteWorkspaceState = { ok: false };

function appwriteError(e: unknown, fallback = "Beklenmeyen bir hata oluştu."): string {
  if (e instanceof AppwriteException) return e.message || fallback;
  return process.env.NODE_ENV !== "production" && e instanceof Error
    ? `${fallback} (${e.message})`
    : fallback;
}

/** Best-effort delete every row where any of `fields` equals tenantId. */
async function purgeTable(table: string, fields: string[], tenantId: string) {
  const { tablesDB } = createAdminClient();
  const ids = new Set<string>();
  for (const field of fields) {
    let offset = 0;
    // Page through to handle tables with more than 500 rows.
    while (true) {
      const result = await tablesDB
        .listRows({
          databaseId: DATABASE_ID,
          tableId: table,
          queries: [Query.equal(field, tenantId), Query.limit(500), Query.offset(offset)],
        })
        .catch(() => ({ rows: [] }));
      for (const row of result.rows) {
        const id = (row as { $id?: string }).$id;
        if (id) ids.add(id);
      }
      if (result.rows.length < 500) break;
      offset += 500;
    }
  }
  await Promise.allSettled(
    Array.from(ids).map((id) =>
      tablesDB.deleteRow(DATABASE_ID, table, id),
    ),
  );
}

/**
 * Hard-delete an entire workspace and everything it owns. Reversible only
 * via your own backup — Appwrite has no undo. Caller must be owner of the
 * tenant and must confirm by typing the company name back to us.
 */
export async function deleteWorkspaceAction(
  _prev: DeleteWorkspaceState,
  formData: FormData,
): Promise<DeleteWorkspaceState> {
  const confirm = String(formData.get("confirm") ?? "").trim();
  let ctx;
  try {
    ctx = await requireTenant();
    requireRole(ctx, ["owner"]);
  } catch {
    return { ok: false, error: "Yalnızca sahip silebilir." };
  }
  const expected = ctx.settings?.companyName?.trim() ?? "";
  if (!expected || confirm !== expected) {
    return {
      ok: false,
      error: "Onaylamak için çalışma alanı adını birebir yazmanız gerekiyor.",
    };
  }

  const tenantId = ctx.tenantId;
  const { tablesDB, storage, teams } = createAdminClient();

  // 1) Wipe Storage objects we still own (logo + any non-archived job files).
  try {
    const settingsRes = await tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.tenantSettings,
      queries: [Query.equal("tenantId", tenantId), Query.limit(1)],
    });
    const settings = (settingsRes.rows[0] as unknown as TenantSettings | undefined) ?? null;
    if (settings?.logo) {
      try {
        await storage.deleteFile(BUCKETS.tenantLogos, settings.logo);
      } catch {
        /* ignore */
      }
    }

    const files = await tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.jobFiles,
      queries: [
        Query.or([
          Query.equal("clinicTenantId", tenantId),
          Query.equal("labTenantId", tenantId),
        ]),
        Query.limit(1000),
      ],
    });
    await Promise.allSettled(
      (files.rows as unknown as JobFile[]).map(async (f) => {
        if (f.archivedAt) return;
        try {
          await storage.deleteFile(BUCKETS.jobFiles, f.fileId);
        } catch {
          /* ignore */
        }
      }),
    );
  } catch {
    /* best-effort */
  }

  // 2) Purge all DB tables tied to this tenant. Order doesn't matter
  // because everything is hard-deleted.
  const tablePurges: Array<[string, string[]]> = [
    [TABLES.notifications, ["tenantId"]],
    [TABLES.auditLogs, ["tenantId"]],
    [TABLES.payments, ["tenantId", "counterpartTenantId"]],
    [TABLES.financeEntries, ["tenantId"]],
    [TABLES.jobStatusHistory, ["clinicTenantId", "labTenantId"]],
    [TABLES.jobFiles, ["clinicTenantId", "labTenantId"]],
    [TABLES.jobs, ["clinicTenantId", "labTenantId"]],
    [TABLES.clinicPricing, ["labTenantId", "clinicTenantId"]],
    [TABLES.prosthetics, ["tenantId"]],
    [TABLES.patients, ["clinicTenantId"]],
    [TABLES.connections, ["clinicTenantId", "labTenantId"]],
    [TABLES.profiles, ["tenantId"]],
    [TABLES.tenantSettings, ["tenantId"]],
  ];
  for (const [table, fields] of tablePurges) {
    await purgeTable(table, fields, tenantId);
  }

  // 3) Finally delete the Appwrite Team itself. This boots every member's
  // permission to read anything that might have slipped through above.
  try {
    await teams.delete({ teamId: tenantId });
  } catch (e) {
    return { ok: false, error: appwriteError(e, "Çalışma alanı silindi ama takım kaldı.") };
  }

  // Drop session/active-tenant cookies — the user is also effectively
  // signed out of this workspace.
  const cookieStore = await cookies();
  cookieStore.delete(ACTIVE_TENANT_COOKIE);
  // Keep the Appwrite session itself so the user can still re-onboard or
  // hop to another workspace they own. If they want to drop the session,
  // there's already a 'Çıkış yap' button.
  void APPWRITE_SESSION_COOKIE; // referenced to avoid unused import

  revalidatePath("/");
  redirect("/onboarding");
}
