import fs from "node:fs/promises";
import path from "node:path";
import dotenv from "dotenv";
import { createClient, type SupabaseClient, type User } from "@supabase/supabase-js";
import { sponsorPhoneIdentity } from "../src/lib/sponsor-account";
import { normalizePhone } from "../src/lib/utils";

dotenv.config({ path: ".env" });

type SuppliedAccount = {
  fullName: string;
  originalPhone: string;
  suppliedUsername: string;
};

type ValidAccount = SuppliedAccount & {
  phone: string;
  loginUsername: string;
};

type Summary = {
  eligible: number;
  created: number;
  existing: number;
  invalid: number;
  failed: number;
};

function argument(name: string): string | null {
  const index = process.argv.indexOf(`--${name}`);
  return index >= 0 ? process.argv[index + 1]?.trim() || null : null;
}

function isServerKey(key: string): boolean {
  if (key.startsWith("sb_secret_")) return true;

  try {
    const payload = key.split(".")[1];
    if (!payload) return false;
    const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8")) as { role?: string };
    return claims.role === "service_role";
  } catch {
    return false;
  }
}

function parseAccounts(markdown: string): SuppliedAccount[] {
  const rowPattern = /^\|\s*(.*?)\s*\|\s*(\+\d+)\s*\|\s*(\d+)\s*\|$/u;
  return markdown
    .split(/\r?\n/)
    .map((line) => line.match(rowPattern))
    .filter((match): match is RegExpMatchArray => Boolean(match))
    .map((match) => ({
      fullName: match[1].trim(),
      originalPhone: match[2],
      suppliedUsername: match[3]
    }));
}

function validateAccounts(accounts: SuppliedAccount[]): {
  valid: ValidAccount[];
  invalid: number;
} {
  const phoneCounts = new Map<string, number>();
  const usernameCounts = new Map<string, number>();

  for (const account of accounts) {
    phoneCounts.set(account.originalPhone, (phoneCounts.get(account.originalPhone) ?? 0) + 1);
    usernameCounts.set(account.suppliedUsername, (usernameCounts.get(account.suppliedUsername) ?? 0) + 1);
  }

  const valid: ValidAccount[] = [];
  let invalid = 0;

  for (const account of accounts) {
    const identity = sponsorPhoneIdentity(account.originalPhone);
    const isDuplicateInput =
      (phoneCounts.get(account.originalPhone) ?? 0) > 1 ||
      (usernameCounts.get(account.suppliedUsername) ?? 0) > 1;
    const hasValidName = account.fullName.length >= 3 && account.fullName.length <= 120;

    if (
      !identity ||
      identity.phone !== account.originalPhone ||
      identity.loginUsername !== account.suppliedUsername ||
      !hasValidName ||
      isDuplicateInput
    ) {
      invalid += 1;
      continue;
    }

    valid.push({
      ...account,
      phone: identity.phone,
      loginUsername: identity.loginUsername
    });
  }

  return { valid, invalid };
}

async function listAllAuthUsers(supabase: SupabaseClient): Promise<User[]> {
  const users: User[] = [];
  const perPage = 1_000;

  for (let page = 1; ; page += 1) {
    const { data, error } = await supabase.auth.admin.listUsers({ page, perPage });
    if (error) throw new Error("Unable to inspect existing Auth accounts.");
    users.push(...data.users);
    if (data.users.length < perPage) break;
  }

  return users;
}

async function main() {
  const sourcePath = argument("file");
  const apply = process.argv.includes("--apply");
  const projectId = process.env.SUPABASE_PROJECT_ID?.trim();
  const url = process.env.NEXT_PUBLIC_SUPABASE_URL?.trim() || (projectId ? `https://${projectId}.supabase.co` : null);
  const key = process.env.SUPABASE_SECRET_KEY?.trim();

  if (!sourcePath) {
    throw new Error('Usage: npm run import-sponsors -- --file "accounts.md" [--apply]');
  }
  if (!url || !key) {
    throw new Error("Set SUPABASE_PROJECT_ID (or NEXT_PUBLIC_SUPABASE_URL) and SUPABASE_SECRET_KEY.");
  }
  if (!isServerKey(key)) {
    throw new Error("SUPABASE_SECRET_KEY must be a Supabase secret key or legacy service-role JWT.");
  }

  const markdown = await fs.readFile(path.resolve(sourcePath), "utf8");
  const parsedAccounts = parseAccounts(markdown);
  if (parsedAccounts.length === 0) throw new Error("No sponsor account rows were found in the supplied file.");

  const validated = validateAccounts(parsedAccounts);
  const summary: Summary = {
    eligible: 0,
    created: 0,
    existing: 0,
    invalid: validated.invalid,
    failed: 0
  };
  const failureReasons = new Map<string, number>();
  const fail = (reason: string) => {
    summary.failed += 1;
    failureReasons.set(reason, (failureReasons.get(reason) ?? 0) + 1);
  };

  const supabase = createClient(url, key, {
    auth: { autoRefreshToken: false, persistSession: false }
  });
  const { data: profiles, error: profileListError } = await supabase
    .from("profiles")
    .select("id,role,phone,login_username");
  if (profileListError) {
    throw new Error("Unable to inspect sponsor profiles. Apply the local-login migration before importing.");
  }

  const authUsers = await listAllAuthUsers(supabase);
  const profilePhones = new Set(
    (profiles ?? []).map((profile) => profile.phone).filter((phone): phone is string => Boolean(phone))
  );
  const profileUsernames = new Set(
    (profiles ?? [])
      .map((profile) => profile.login_username)
      .filter((username): username is string => Boolean(username))
  );
  const authPhones = new Set(
    authUsers
      .map((user) => (user.phone ? normalizePhone(user.phone) : null))
      .filter((phone): phone is string => Boolean(phone))
  );
  const authUsernames = new Set(
    authUsers
      .map((user) => {
        const metadataUsername = user.user_metadata?.login_username;
        if (typeof metadataUsername === "string" && metadataUsername.length > 0) {
          return metadataUsername;
        }
        return user.phone ? sponsorPhoneIdentity(user.phone)?.loginUsername ?? null : null;
      })
      .filter((username): username is string => typeof username === "string" && username.length > 0)
  );
  const actorId =
    profiles?.find((profile) => profile.role === "admin")?.id ?? null;

  for (const account of validated.valid) {
    const exists =
      profilePhones.has(account.phone) ||
      profileUsernames.has(account.loginUsername) ||
      authPhones.has(account.phone) ||
      authUsernames.has(account.loginUsername);

    if (exists) {
      summary.existing += 1;
      continue;
    }

    if (!apply) {
      summary.eligible += 1;
      continue;
    }

    const created = await supabase.auth.admin.createUser({
      phone: account.phone,
      password: account.loginUsername,
      phone_confirm: true,
      user_metadata: {
        full_name: account.fullName,
        phone: account.phone,
        login_username: account.loginUsername,
        initial_role: "donor",
        must_change_password: true
      }
    });
    if (created.error || !created.data.user) {
      fail("auth_creation_failed");
      continue;
    }

    const userId = created.data.user.id;
    const { error: profileError } = await supabase.from("profiles").upsert({
      id: userId,
      role: "donor",
      full_name: account.fullName,
      phone: account.phone,
      login_username: account.loginUsername,
      must_change_password: true,
      is_active: true
    });
    if (profileError) {
      const rollback = await supabase.auth.admin.deleteUser(userId);
      fail(rollback.error ? "profile_rollback_failed" : "profile_creation_failed");
      continue;
    }

    await supabase.from("audit_logs").insert({
      actor_id: actorId,
      action: "sponsor.created",
      entity_type: "profile",
      entity_id: userId,
      metadata: { source: "one_time_bulk_import", active: true }
    });

    profilePhones.add(account.phone);
    profileUsernames.add(account.loginUsername);
    authPhones.add(account.phone);
    authUsernames.add(account.loginUsername);
    summary.created += 1;
  }

  console.log(
    apply
      ? `Import summary: created=${summary.created}, existing=${summary.existing}, invalid=${summary.invalid}, failed=${summary.failed}`
      : `Validation summary: eligible=${summary.eligible}, existing=${summary.existing}, invalid=${summary.invalid}, failed=${summary.failed}`
  );
  if (failureReasons.size > 0) {
    console.log(
      `Failure categories: ${Array.from(failureReasons, ([reason, count]) => `${reason}=${count}`).join(", ")}`
    );
    process.exitCode = 1;
  }
}

main().catch((error: unknown) => {
  console.error(error instanceof Error ? error.message : "Sponsor import failed.");
  process.exitCode = 1;
});
