"use server";

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

import { logAudit } from "./audit";
import {
  isPlanLimitError,
  planLimitMessage,
  requirePlanCapacity,
} from "./plan-limits";
import {
  DATABASE_ID,
  TABLES,
  type CustomerSoftware,
  type Software,
} from "./schema";
import { createAdminClient } from "./server";
import { requireTenant } from "./tenant-guard";
import type { SoftwareActionState } from "./software-types";
import { customerSoftwareSchema, softwareSchema } from "@/lib/validation/software";

function appwriteError(e: unknown): string {
  if (e instanceof AppwriteException) {
    return e.message || "Beklenmeyen bir hata oluştu.";
  }
  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 teamRowPermissions(tenantId: string) {
  return [
    Permission.read(Role.team(tenantId)),
    Permission.update(Role.team(tenantId)),
    Permission.delete(Role.team(tenantId, "owner")),
    Permission.delete(Role.team(tenantId, "admin")),
  ];
}

// -------------------- Software (catalog) --------------------

function pickSoftwareFields(formData: FormData) {
  return {
    name: String(formData.get("name") ?? "").trim(),
    version: String(formData.get("version") ?? "").trim(),
    description: String(formData.get("description") ?? "").trim(),
    defaultFee: String(formData.get("defaultFee") ?? ""),
  };
}

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

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

  try {
    await requirePlanCapacity(ctx, "software");
  } catch (e) {
    if (isPlanLimitError(e)) {
      return {
        ok: false,
        error: planLimitMessage(e.resource, e.limit),
        code: "PLAN_LIMIT_EXCEEDED",
      };
    }
    throw e;
  }

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

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

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

export async function updateSoftwareAction(
  _prev: SoftwareActionState,
  formData: FormData,
): Promise<SoftwareActionState> {
  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 = softwareSchema.safeParse(pickSoftwareFields(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.software,
      id,
    )) as unknown as Software;

    if (existing.tenantId !== ctx.tenantId) {
      return { ok: false, error: "Erişim engellendi." };
    }

    await tablesDB.updateRow(DATABASE_ID, TABLES.software, id, parsed.data);

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

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

export async function deleteSoftwareAction(formData: FormData): Promise<SoftwareActionState> {
  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.software,
      id,
    )) as unknown as Software;

    if (existing.tenantId !== ctx.tenantId) {
      return { ok: false, error: "Erişim engellendi." };
    }

    // Detach from all customer_software rows first
    const assignments = await tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.customerSoftware,
      queries: [
        Query.equal("tenantId", ctx.tenantId),
        Query.equal("softwareId", id),
        Query.limit(500),
      ],
    });
    for (const row of assignments.rows) {
      await tablesDB.deleteRow(DATABASE_ID, TABLES.customerSoftware, row.$id);
    }

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

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "delete",
      entityType: "software",
      entityId: id,
      changes: { name: existing.name, detachedAssignments: assignments.rows.length },
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

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

// -------------------- customer_software (assignments) --------------------

function pickAssignmentFields(formData: FormData) {
  return {
    customerId: String(formData.get("customerId") ?? ""),
    softwareId: String(formData.get("softwareId") ?? ""),
    startDate: String(formData.get("startDate") ?? ""),
    endDate: String(formData.get("endDate") ?? ""),
    fee: String(formData.get("fee") ?? ""),
    billingPeriod: (formData.get("billingPeriod") as "monthly" | "yearly" | "onetime" | null) ??
      "monthly",
    notes: String(formData.get("notes") ?? "").trim(),
  };
}

function toIsoDate(v?: string): string | undefined {
  if (!v) return undefined;
  // input type=date sends YYYY-MM-DD; Appwrite expects ISO with timezone
  if (/^\d{4}-\d{2}-\d{2}$/.test(v)) return `${v}T00:00:00.000+00:00`;
  return v;
}

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

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

  try {
    const { tablesDB } = createAdminClient();
    const data = {
      ...parsed.data,
      startDate: toIsoDate(parsed.data.startDate),
      endDate: toIsoDate(parsed.data.endDate),
    };
    const row = await tablesDB.createRow(
      DATABASE_ID,
      TABLES.customerSoftware,
      ID.unique(),
      {
        tenantId: ctx.tenantId,
        createdBy: ctx.user.id,
        ...data,
      },
      teamRowPermissions(ctx.tenantId),
    );

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

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

export async function updateAssignmentAction(
  _prev: SoftwareActionState,
  formData: FormData,
): Promise<SoftwareActionState> {
  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 = customerSoftwareSchema.safeParse(pickAssignmentFields(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.customerSoftware,
      id,
    )) as unknown as CustomerSoftware;

    if (existing.tenantId !== ctx.tenantId) {
      return { ok: false, error: "Erişim engellendi." };
    }

    const data = {
      ...parsed.data,
      startDate: toIsoDate(parsed.data.startDate),
      endDate: toIsoDate(parsed.data.endDate),
    };
    await tablesDB.updateRow(DATABASE_ID, TABLES.customerSoftware, id, data);

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

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

export async function deleteAssignmentAction(formData: FormData): Promise<SoftwareActionState> {
  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.customerSoftware,
      id,
    )) as unknown as CustomerSoftware;

    if (existing.tenantId !== ctx.tenantId) {
      return { ok: false, error: "Erişim engellendi." };
    }

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

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

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