"use server";

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

import { logAudit } from "./audit";
import { createNotification } from "./notification-helpers";
import {
  DATABASE_ID,
  TABLES,
  type Connection,
  type TenantSettings,
} from "./schema";
import { createAdminClient } from "./server";
import { requireRole, requireTenant } from "./tenant-guard";
import type {
  ConnectionActionState,
  ConnectionRequestState,
} from "./connection-types";

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;
}

function connectionPermissions(clinicTenantId: string, labTenantId: string): string[] {
  return [
    Permission.read(Role.team(clinicTenantId)),
    Permission.read(Role.team(labTenantId)),
    Permission.update(Role.team(clinicTenantId, "owner")),
    Permission.update(Role.team(clinicTenantId, "admin")),
    Permission.update(Role.team(labTenantId, "owner")),
    Permission.update(Role.team(labTenantId, "admin")),
    Permission.delete(Role.team(clinicTenantId, "owner")),
    Permission.delete(Role.team(labTenantId, "owner")),
  ];
}

export async function requestConnectionAction(
  _prev: ConnectionRequestState,
  formData: FormData,
): Promise<ConnectionRequestState> {
  const rawCode = String(formData.get("memberNumber") ?? "").trim().toUpperCase();
  if (!rawCode) {
    return { ok: false, error: "Bağlantı kodu zorunlu." };
  }

  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Oturum bulunamadı." };
  }
  if (!ctx.kind) {
    return { ok: false, error: "Hesap türünüz tanımlı değil. Ayarlar > Çalışma alanı." };
  }

  try {
    const { tablesDB } = createAdminClient();

    // Resolve counterpart by memberNumber
    const counterpartRes = await tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.tenantSettings,
      queries: [Query.equal("memberNumber", rawCode), Query.limit(1)],
    });
    const counterpart = counterpartRes.rows[0] as unknown as TenantSettings | undefined;
    if (!counterpart) {
      return { ok: false, error: "Bu kod ile eşleşen bir hesap bulunamadı." };
    }
    if (counterpart.tenantId === ctx.tenantId) {
      return { ok: false, error: "Kendi kodunuzla bağlantı kuramazsınız." };
    }
    if (counterpart.kind === ctx.kind) {
      const same = ctx.kind === "lab" ? "laboratuvar" : "klinik";
      return {
        ok: false,
        error: `Bu kod bir ${same} hesabına ait. Bağlantı sadece klinik ↔ laboratuvar arasında olur.`,
      };
    }

    const clinicTenantId = ctx.kind === "clinic" ? ctx.tenantId : counterpart.tenantId;
    const labTenantId = ctx.kind === "lab" ? ctx.tenantId : counterpart.tenantId;

    // Already connected or pending?
    const existingRes = await tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.connections,
      queries: [
        Query.equal("clinicTenantId", clinicTenantId),
        Query.equal("labTenantId", labTenantId),
        Query.limit(1),
      ],
    });
    const existing = existingRes.rows[0] as unknown as Connection | undefined;
    if (existing) {
      if (existing.status === "approved") {
        return { ok: false, error: "Bu hesapla zaten bağlantınız var." };
      }
      if (existing.status === "pending") {
        return { ok: false, error: "Bekleyen bir talep zaten var." };
      }
      // status === 'rejected' → re-open: reset to pending, requesterBy = current user
      await tablesDB.updateRow(DATABASE_ID, TABLES.connections, existing.$id, {
        status: "pending",
        requestedBy: ctx.user.id,
        requestedAt: new Date().toISOString(),
        approvedAt: null,
        rejectedAt: null,
      });
      await logAudit({
        tenantId: ctx.tenantId,
        userId: ctx.user.id,
        action: "update",
        entityType: "connection",
        entityId: existing.$id,
        changes: { status: "pending", reopened: true },
      });
    } else {
      const created = await tablesDB.createRow(
        DATABASE_ID,
        TABLES.connections,
        ID.unique(),
        {
          clinicTenantId,
          labTenantId,
          status: "pending",
          requestedBy: ctx.user.id,
          requestedAt: new Date().toISOString(),
        },
        connectionPermissions(clinicTenantId, labTenantId),
      );
      await logAudit({
        tenantId: ctx.tenantId,
        userId: ctx.user.id,
        action: "create",
        entityType: "connection",
        entityId: created.$id,
        changes: { clinicTenantId, labTenantId, status: "pending" },
      });
      const counterpartId = counterpart.tenantId;
      await createNotification({
        tenantId: counterpartId,
        connectionId: created.$id,
        message: `${ctx.settings?.companyName ?? "Bir hesap"} bağlantı talebi gönderdi.`,
      });
    }
  } catch (e) {
    return { ok: false, error: appwriteError(e, "Bağlantı talebi gönderilemedi.") };
  }

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

async function loadConnectionForMutation(
  connectionId: string,
  expectedTenant: string,
): Promise<Connection | null> {
  try {
    const { tablesDB } = createAdminClient();
    const row = await tablesDB.getRow(DATABASE_ID, TABLES.connections, connectionId);
    const conn = row as unknown as Connection;
    if (
      conn.clinicTenantId !== expectedTenant &&
      conn.labTenantId !== expectedTenant
    ) {
      return null;
    }
    return conn;
  } catch {
    return null;
  }
}

export async function approveConnectionAction(
  _prev: ConnectionActionState,
  formData: FormData,
): Promise<ConnectionActionState> {
  const connectionId = String(formData.get("connectionId") ?? "").trim();
  if (!connectionId) return { ok: false, error: "Bağlantı bulunamadı." };

  let ctx;
  try {
    ctx = await requireTenant();
    requireRole(ctx, ["owner", "admin"]);
  } catch {
    return { ok: false, error: "Bu işlem için yetkiniz yok." };
  }

  const conn = await loadConnectionForMutation(connectionId, ctx.tenantId);
  if (!conn) return { ok: false, error: "Bağlantı bulunamadı." };
  if (conn.status !== "pending") {
    return { ok: false, error: "Bu talep zaten yanıtlandı." };
  }
  // Requester cannot self-approve
  const requesterTenant =
    conn.requestedBy && conn.clinicTenantId === ctx.tenantId
      ? conn.clinicTenantId
      : conn.labTenantId;
  // Better: simply require that current tenant is NOT the side that initiated.
  // We don't have requesterTenant explicit on row, but requestedBy.userId belongs
  // to one side. As a guard, approver must be on the other side from the user
  // who created it. Best signal we have: requestedBy is the originator userId.
  // If approver is same user as requester → block.
  if (conn.requestedBy === ctx.user.id) {
    return { ok: false, error: "Kendi talebinizi siz onaylayamazsınız." };
  }
  void requesterTenant;

  try {
    const { tablesDB } = createAdminClient();
    await tablesDB.updateRow(DATABASE_ID, TABLES.connections, connectionId, {
      status: "approved",
      approvedAt: new Date().toISOString(),
      rejectedAt: null,
    });
    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "update",
      entityType: "connection",
      entityId: connectionId,
      changes: { status: "approved" },
    });
    const requesterTenant =
      conn.clinicTenantId === ctx.tenantId ? conn.labTenantId : conn.clinicTenantId;
    await createNotification({
      tenantId: requesterTenant,
      connectionId,
      message: `${ctx.settings?.companyName ?? "Karşı taraf"} bağlantı talebinizi onayladı.`,
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e, "Onaylanamadı.") };
  }

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

export async function rejectConnectionAction(
  _prev: ConnectionActionState,
  formData: FormData,
): Promise<ConnectionActionState> {
  const connectionId = String(formData.get("connectionId") ?? "").trim();
  if (!connectionId) return { ok: false, error: "Bağlantı bulunamadı." };

  let ctx;
  try {
    ctx = await requireTenant();
    requireRole(ctx, ["owner", "admin"]);
  } catch {
    return { ok: false, error: "Bu işlem için yetkiniz yok." };
  }

  const conn = await loadConnectionForMutation(connectionId, ctx.tenantId);
  if (!conn) return { ok: false, error: "Bağlantı bulunamadı." };
  if (conn.status !== "pending") {
    return { ok: false, error: "Bu talep zaten yanıtlandı." };
  }
  if (conn.requestedBy === ctx.user.id) {
    return { ok: false, error: "Kendi talebinizi reddedemezsiniz." };
  }

  try {
    const { tablesDB } = createAdminClient();
    await tablesDB.updateRow(DATABASE_ID, TABLES.connections, connectionId, {
      status: "rejected",
      rejectedAt: new Date().toISOString(),
    });
    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "update",
      entityType: "connection",
      entityId: connectionId,
      changes: { status: "rejected" },
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e, "Reddedilemedi.") };
  }

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

export async function cancelConnectionAction(
  _prev: ConnectionActionState,
  formData: FormData,
): Promise<ConnectionActionState> {
  // Used when the requester wants to withdraw their own pending request.
  const connectionId = String(formData.get("connectionId") ?? "").trim();
  if (!connectionId) return { ok: false, error: "Bağlantı bulunamadı." };

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

  const conn = await loadConnectionForMutation(connectionId, ctx.tenantId);
  if (!conn) return { ok: false, error: "Bağlantı bulunamadı." };
  if (conn.status !== "pending") {
    return { ok: false, error: "Bu talep iptal edilemez." };
  }
  if (conn.requestedBy !== ctx.user.id) {
    return { ok: false, error: "Sadece talebi gönderen iptal edebilir." };
  }

  try {
    const { tablesDB } = createAdminClient();
    await tablesDB.deleteRow(DATABASE_ID, TABLES.connections, connectionId);
    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "delete",
      entityType: "connection",
      entityId: connectionId,
      changes: { status: "pending", reason: "cancelled" },
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e, "İptal edilemedi.") };
  }

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

export async function deleteConnectionAction(
  _prev: ConnectionActionState,
  formData: FormData,
): Promise<ConnectionActionState> {
  const connectionId = String(formData.get("connectionId") ?? "").trim();
  if (!connectionId) return { ok: false, error: "Bağlantı bulunamadı." };

  let ctx;
  try {
    ctx = await requireTenant();
    requireRole(ctx, ["owner", "admin"]);
  } catch {
    return { ok: false, error: "Bu işlem için yetkiniz yok." };
  }

  const conn = await loadConnectionForMutation(connectionId, ctx.tenantId);
  if (!conn) return { ok: false, error: "Bağlantı bulunamadı." };

  try {
    const { tablesDB } = createAdminClient();
    await tablesDB.deleteRow(DATABASE_ID, TABLES.connections, connectionId);
    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "delete",
      entityType: "connection",
      entityId: connectionId,
      changes: { previousStatus: conn.status },
    });
  } catch (e) {
    return { ok: false, error: appwriteError(e, "Silinemedi.") };
  }

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