"use server";

import { revalidatePath } from "next/cache";
import { AppwriteException, ID, Query } from "node-appwrite";
import { z } from "zod";

import { logAudit } from "./audit";
import {
  DATABASE_ID,
  TABLES,
  type CreditCard,
  type CreditCardStatement,
} from "./schema";
import { canAccessRow, scopedRowPermissions } from "./scope-permissions";
import { createAdminClient } from "./server";
import { requireTenant } from "./tenant-guard";
import type { CreditCardActionState } from "./credit-card-types";
import { creditCardSchema, statementSchema } from "@/lib/validation/credit-cards";

function appwriteError(e: unknown): string {
  if (e instanceof AppwriteException) return e.message || "Beklenmeyen hata.";
  return "Bağlantı hatası. Tekrar deneyin.";
}

function flattenErrors(err: z.ZodError): Record<string, string> {
  const out: Record<string, string> = {};
  for (const issue of err.issues) {
    const key = issue.path.join(".");
    if (key && !out[key]) out[key] = issue.message;
  }
  return out;
}

function toIso(v: string): string {
  if (!v) return v;
  if (/^\d{4}-\d{2}-\d{2}$/.test(v)) return `${v}T00:00:00.000+00:00`;
  return v;
}

// ---------------- Cards ----------------

function pickCardFields(formData: FormData) {
  return {
    bankName: String(formData.get("bankName") ?? "").trim(),
    cardName: String(formData.get("cardName") ?? "").trim(),
    last4: String(formData.get("last4") ?? "").trim(),
    creditLimit: String(formData.get("creditLimit") ?? "0"),
    statementDay: String(formData.get("statementDay") ?? "1"),
    dueDay: String(formData.get("dueDay") ?? "10"),
    interestRate: String(formData.get("interestRate") ?? "4.25"),
    bankAccountId: String(formData.get("bankAccountId") ?? ""),
    notes: String(formData.get("notes") ?? "").trim(),
    scope: (formData.get("scope") as "company" | "personal" | null) ?? "company",
  };
}

export async function createCreditCardAction(
  _prev: CreditCardActionState,
  formData: FormData,
): Promise<CreditCardActionState> {
  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Yetkiniz yok." };
  }

  const parsed = creditCardSchema.safeParse(pickCardFields(formData));
  if (!parsed.success) {
    return { ok: false, error: "Form geçersiz.", fieldErrors: flattenErrors(parsed.error) };
  }

  try {
    const { tablesDB } = createAdminClient();
    const row = await tablesDB.createRow(
      DATABASE_ID,
      TABLES.creditCards,
      ID.unique(),
      {
        tenantId: ctx.tenantId,
        createdBy: ctx.user.id,
        ...parsed.data,
      },
      scopedRowPermissions(ctx.tenantId, ctx.user.id, parsed.data.scope),
    );

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "create",
      entityType: "credit_card",
      entityId: row.$id,
      changes: parsed.data,
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

  revalidatePath("/finance/cards");
  return { ok: true };
}

export async function updateCreditCardAction(
  _prev: CreditCardActionState,
  formData: FormData,
): Promise<CreditCardActionState> {
  const id = String(formData.get("id") ?? "");
  if (!id) return { ok: false, error: "ID eksik." };

  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Yetkiniz yok." };
  }

  const parsed = creditCardSchema.safeParse(pickCardFields(formData));
  if (!parsed.success) {
    return { ok: false, error: "Form geçersiz.", fieldErrors: flattenErrors(parsed.error) };
  }

  try {
    const { tablesDB } = createAdminClient();
    const existing = (await tablesDB.getRow(
      DATABASE_ID,
      TABLES.creditCards,
      id,
    )) as unknown as CreditCard;
    if (existing.tenantId !== ctx.tenantId || !canAccessRow(existing, ctx.user.id)) {
      return { ok: false, error: "Erişim engellendi." };
    }

    await tablesDB.updateRow(
      DATABASE_ID,
      TABLES.creditCards,
      id,
      parsed.data,
      scopedRowPermissions(ctx.tenantId, existing.createdBy, parsed.data.scope),
    );

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "update",
      entityType: "credit_card",
      entityId: id,
      changes: parsed.data,
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

  revalidatePath("/finance/cards");
  return { ok: true };
}

export async function archiveCreditCardAction(
  formData: FormData,
): Promise<CreditCardActionState> {
  const id = String(formData.get("id") ?? "");
  if (!id) return { ok: false, error: "ID eksik." };

  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Yetkiniz yok." };
  }

  try {
    const { tablesDB } = createAdminClient();
    const existing = (await tablesDB.getRow(
      DATABASE_ID,
      TABLES.creditCards,
      id,
    )) as unknown as CreditCard;
    if (existing.tenantId !== ctx.tenantId || !canAccessRow(existing, ctx.user.id)) {
      return { ok: false, error: "Erişim engellendi." };
    }
    const newArchivedState = !existing.archived;
    await tablesDB.updateRow(DATABASE_ID, TABLES.creditCards, id, {
      archived: newArchivedState,
    });

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "update",
      entityType: "credit_card",
      entityId: id,
      changes: { archived: newArchivedState },
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

  revalidatePath("/finance/cards");
  return { ok: true };
}

export async function deleteCreditCardAction(
  formData: FormData,
): Promise<CreditCardActionState> {
  const id = String(formData.get("id") ?? "");
  if (!id) return { ok: false, error: "ID eksik." };

  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Yetkiniz yok." };
  }

  try {
    const { tablesDB } = createAdminClient();
    const existing = (await tablesDB.getRow(
      DATABASE_ID,
      TABLES.creditCards,
      id,
    )) as unknown as CreditCard;
    if (existing.tenantId !== ctx.tenantId || !canAccessRow(existing, ctx.user.id)) {
      return { ok: false, error: "Erişim engellendi." };
    }

    // Cascade delete statements + their finance entries
    const statements = await tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.creditCardStatements,
      queries: [
        Query.equal("tenantId", ctx.tenantId),
        Query.equal("cardId", id),
        Query.limit(500),
      ],
    });
    for (const s of statements.rows as unknown as CreditCardStatement[]) {
      if (s.financeEntryId) {
        try {
          await tablesDB.deleteRow(DATABASE_ID, TABLES.financeEntries, s.financeEntryId);
        } catch {
          /* ignore */
        }
      }
      await tablesDB.deleteRow(DATABASE_ID, TABLES.creditCardStatements, s.$id);
    }

    await tablesDB.deleteRow(DATABASE_ID, TABLES.creditCards, id);

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "delete",
      entityType: "credit_card",
      entityId: id,
      changes: { bankName: existing.bankName, cardName: existing.cardName, statements: statements.rows.length },
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

  revalidatePath("/finance/cards");
  return { ok: true };
}

// ---------------- Statements ----------------

function pickStatementFields(formData: FormData) {
  return {
    cardId: String(formData.get("cardId") ?? ""),
    period: String(formData.get("period") ?? "").trim(),
    statementDate: String(formData.get("statementDate") ?? ""),
    dueDate: String(formData.get("dueDate") ?? ""),
    totalDebt: String(formData.get("totalDebt") ?? "0"),
    minimumPayment: String(formData.get("minimumPayment") ?? "0"),
    notes: String(formData.get("notes") ?? "").trim(),
  };
}

function computeStatus(
  totalDebt: number,
  paidAmount: number,
  dueDate: string,
): "pending" | "partial" | "paid" | "overdue" {
  if (paidAmount >= totalDebt) return "paid";
  const past = new Date(dueDate).getTime() < Date.now();
  if (paidAmount > 0) return past ? "overdue" : "partial";
  return past ? "overdue" : "pending";
}

export async function createStatementAction(
  _prev: CreditCardActionState,
  formData: FormData,
): Promise<CreditCardActionState> {
  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Yetkiniz yok." };
  }

  const parsed = statementSchema.safeParse(pickStatementFields(formData));
  if (!parsed.success) {
    return { ok: false, error: "Form geçersiz.", fieldErrors: flattenErrors(parsed.error) };
  }

  try {
    const { tablesDB } = createAdminClient();
    const card = (await tablesDB.getRow(
      DATABASE_ID,
      TABLES.creditCards,
      parsed.data.cardId,
    )) as unknown as CreditCard;
    if (card.tenantId !== ctx.tenantId || !canAccessRow(card, ctx.user.id)) {
      return { ok: false, error: "Erişim engellendi." };
    }

    const status = computeStatus(parsed.data.totalDebt, 0, parsed.data.dueDate);
    // Statements inherit the card's scope.
    const cardScope = card.scope ?? "company";

    const row = await tablesDB.createRow(
      DATABASE_ID,
      TABLES.creditCardStatements,
      ID.unique(),
      {
        tenantId: ctx.tenantId,
        createdBy: ctx.user.id,
        cardId: parsed.data.cardId,
        period: parsed.data.period,
        statementDate: toIso(parsed.data.statementDate),
        dueDate: toIso(parsed.data.dueDate),
        totalDebt: parsed.data.totalDebt,
        minimumPayment: parsed.data.minimumPayment,
        paidAmount: 0,
        status,
        notes: parsed.data.notes,
      },
      scopedRowPermissions(ctx.tenantId, ctx.user.id, cardScope),
    );

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "create",
      entityType: "credit_card_statement",
      entityId: row.$id,
      changes: { cardId: parsed.data.cardId, period: parsed.data.period, totalDebt: parsed.data.totalDebt },
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

  revalidatePath("/finance/cards");
  return { ok: true };
}

export async function payStatementAction(formData: FormData): Promise<CreditCardActionState> {
  const id = String(formData.get("id") ?? "");
  const amountStr = String(formData.get("amount") ?? "");
  if (!id) return { ok: false, error: "ID eksik." };

  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Yetkiniz yok." };
  }

  try {
    const { tablesDB } = createAdminClient();
    const existing = (await tablesDB.getRow(
      DATABASE_ID,
      TABLES.creditCardStatements,
      id,
    )) as unknown as CreditCardStatement;
    if (existing.tenantId !== ctx.tenantId) {
      return { ok: false, error: "Erişim engellendi." };
    }

    const remaining = (existing.totalDebt ?? 0) - (existing.paidAmount ?? 0);
    if (remaining <= 0) {
      return { ok: false, error: "Bu ekstrenin bakiyesi kalmamış." };
    }

    const amount = amountStr
      ? Number(amountStr.replace(",", "."))
      : remaining;
    if (!Number.isFinite(amount) || amount <= 0) {
      return { ok: false, error: "Tutar geçersiz.", fieldErrors: { amount: "Geçersiz" } };
    }
    const payAmount = Math.min(amount, remaining);

    const card = (await tablesDB.getRow(
      DATABASE_ID,
      TABLES.creditCards,
      existing.cardId,
    )) as unknown as CreditCard;
    if (!canAccessRow(card, ctx.user.id)) {
      return { ok: false, error: "Erişim engellendi." };
    }
    const cardScope = card.scope ?? "company";

    const fe = await tablesDB.createRow(
      DATABASE_ID,
      TABLES.financeEntries,
      ID.unique(),
      {
        tenantId: ctx.tenantId,
        createdBy: ctx.user.id,
        type: "expense",
        amount: payAmount,
        date: new Date().toISOString(),
        description: `${card.bankName} ${card.cardName} ${existing.period} ekstre ödemesi`,
        bankAccountId: card.bankAccountId,
        scope: cardScope,
      },
      scopedRowPermissions(ctx.tenantId, ctx.user.id, cardScope),
    );

    const newPaid = (existing.paidAmount ?? 0) + payAmount;
    const newStatus = computeStatus(existing.totalDebt ?? 0, newPaid, existing.dueDate);

    await tablesDB.updateRow(DATABASE_ID, TABLES.creditCardStatements, id, {
      paidAmount: Number(newPaid.toFixed(2)),
      status: newStatus,
      financeEntryId: newStatus === "paid" ? fe.$id : existing.financeEntryId ?? fe.$id,
    });

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "update",
      entityType: "credit_card_statement",
      entityId: id,
      changes: { paidAmount: newPaid, status: newStatus, payAmount },
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

  revalidatePath("/finance/cards");
  revalidatePath("/finance");
  return { ok: true };
}

export async function deleteStatementAction(
  formData: FormData,
): Promise<CreditCardActionState> {
  const id = String(formData.get("id") ?? "");
  if (!id) return { ok: false, error: "ID eksik." };

  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Yetkiniz yok." };
  }

  try {
    const { tablesDB } = createAdminClient();
    const existing = (await tablesDB.getRow(
      DATABASE_ID,
      TABLES.creditCardStatements,
      id,
    )) as unknown as CreditCardStatement;
    if (existing.tenantId !== ctx.tenantId) {
      return { ok: false, error: "Erişim engellendi." };
    }
    // Statement inherits its parent card's scope.
    const parent = (await tablesDB.getRow(
      DATABASE_ID,
      TABLES.creditCards,
      existing.cardId,
    )) as unknown as CreditCard;
    if (!canAccessRow(parent, ctx.user.id)) {
      return { ok: false, error: "Erişim engellendi." };
    }
    if (existing.financeEntryId) {
      try {
        await tablesDB.deleteRow(
          DATABASE_ID,
          TABLES.financeEntries,
          existing.financeEntryId,
        );
      } catch {
        /* ignore */
      }
    }
    await tablesDB.deleteRow(DATABASE_ID, TABLES.creditCardStatements, id);

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "delete",
      entityType: "credit_card_statement",
      entityId: id,
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

  revalidatePath("/finance/cards");
  revalidatePath("/finance");
  return { ok: true };
}
