"use server";

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

import { logAudit } from "./audit";
import { DATABASE_ID, TABLES, type InviteLink, type InviteRole } from "./schema";
import { createAdminClient, createSessionClient } from "./server";
import { requireRole, requireTenant } from "./tenant-guard";
import type { InviteState, MemberActionState } from "./team-types";
import { ACTIVE_TENANT_COOKIE } from "./tenant-types";

const APP_URL = process.env.APP_URL ?? "http://localhost:3000";
const INVITE_TTL_DAYS = 7;

function appwriteError(e: unknown): string {
  if (e instanceof AppwriteException) {
    if (e.type === "team_invite_already_exists") return "Bu kişi zaten davetli.";
    if (e.type === "team_membership_already_confirmed") return "Bu kişi zaten ekipte.";
    if (e.type === "user_not_found") return "Kullanıcı bulunamadı.";
    return e.message || "Beklenmeyen bir hata oluştu.";
  }
  return "Bağlantı hatası. Tekrar deneyin.";
}

function generateCode(length = 8): string {
  const chars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";
  let out = "";
  const bytes = new Uint8Array(length);
  crypto.getRandomValues(bytes);
  for (let i = 0; i < length; i++) out += chars[bytes[i] % chars.length];
  return out;
}

async function findUserByEmail(email: string) {
  const { users } = createAdminClient();
  const result = await users.list({
    queries: [Query.equal("email", email), Query.limit(1)],
  });
  return result.users[0] ?? null;
}

export async function inviteMemberAction(
  _prev: InviteState,
  formData: FormData,
): Promise<InviteState> {
  const email = String(formData.get("email") ?? "").trim().toLowerCase();
  const role = (String(formData.get("role") ?? "member") as InviteRole) === "admin"
    ? "admin"
    : "member";

  if (!email || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
    return { ok: false, error: "Geçerli bir email girin." };
  }

  let ctx;
  try {
    ctx = await requireTenant();
    requireRole(ctx, ["owner", "admin"]);
  } catch {
    return { ok: false, error: "Üye davet etme yetkiniz yok." };
  }

  if (email === ctx.user.email.toLowerCase()) {
    return { ok: false, error: "Kendinizi davet edemezsiniz." };
  }

  const admin = createAdminClient();

  // 1. Kullanıcı zaten Appwrite'ta var mı?
  let existingUser = null;
  try {
    existingUser = await findUserByEmail(email);
  } catch {
    /* ignore */
  }

  // 2. Zaten ekipte mi?
  if (existingUser) {
    try {
      const memberships = await admin.teams.listMemberships(ctx.tenantId);
      if (memberships.memberships.some((m) => m.userId === existingUser!.$id)) {
        return { ok: false, error: "Bu kullanıcı zaten ekipte." };
      }
    } catch {
      /* ignore */
    }
  }

  // 3. Pending davet var mı?
  try {
    const existing = await admin.tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.inviteLinks,
      queries: [
        Query.equal("tenantId", ctx.tenantId),
        Query.equal("email", email),
        Query.equal("status", "pending"),
        Query.limit(1),
      ],
    });
    const found = existing.rows[0] as unknown as InviteLink | undefined;
    if (found) {
      return {
        ok: true,
        shortUrl: `${APP_URL}/d/${found.code}`,
        message: "Bu email için zaten aktif bir davet var. Mevcut linki kopyaladık.",
      };
    }
  } catch {
    /* continue */
  }

  // 4. Yeni davet linki üret
  const code = generateCode(8);
  const expiresAt = new Date(Date.now() + INVITE_TTL_DAYS * 24 * 60 * 60 * 1000).toISOString();

  try {
    const row = await admin.tablesDB.createRow(
      DATABASE_ID,
      TABLES.inviteLinks,
      ID.unique(),
      {
        tenantId: ctx.tenantId,
        code,
        email,
        role,
        status: "pending",
        invitedBy: ctx.user.id,
        expiresAt,
      },
      [
        Permission.read(Role.team(ctx.tenantId)),
        Permission.update(Role.team(ctx.tenantId, "owner")),
        Permission.update(Role.team(ctx.tenantId, "admin")),
        Permission.delete(Role.team(ctx.tenantId, "owner")),
        Permission.delete(Role.team(ctx.tenantId, "admin")),
      ],
    );

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

  revalidatePath("/settings/members");
  return {
    ok: true,
    shortUrl: `${APP_URL}/d/${code}`,
    message: existingUser
      ? "Davet linki oluşturuldu. Linki bu kişiye iletin; tıklayınca giriş yapıp katılabilir."
      : "Davet linki oluşturuldu. Linki bu kişiye iletin; tıklayınca hesap açıp katılabilir.",
  };
}

export async function cancelInviteAction(
  _prev: MemberActionState,
  formData: FormData,
): Promise<MemberActionState> {
  const inviteId = String(formData.get("inviteId") ?? "");
  if (!inviteId) return { ok: false, error: "Davet bulunamadı." };

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

  try {
    const { tablesDB } = createAdminClient();
    await tablesDB.updateRow(DATABASE_ID, TABLES.inviteLinks, inviteId, {
      status: "cancelled",
    });

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

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

export async function removeMemberAction(
  _prev: MemberActionState,
  formData: FormData,
): Promise<MemberActionState> {
  const membershipId = String(formData.get("membershipId") ?? "");
  if (!membershipId) return { ok: false, error: "Üyelik bulunamadı." };

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

  try {
    const { teams } = createAdminClient();
    const memberships = await teams.listMemberships(ctx.tenantId);
    const target = memberships.memberships.find((m) => m.$id === membershipId);

    if (!target) return { ok: false, error: "Üyelik bulunamadı." };
    if (target.userId === ctx.user.id) {
      return { ok: false, error: "Kendinizi çıkaramazsınız." };
    }
    if (target.roles.includes("owner") && ctx.role !== "owner") {
      return { ok: false, error: "Sahibi yalnızca başka bir sahip kaldırabilir." };
    }

    await teams.deleteMembership(ctx.tenantId, membershipId);

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

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

/**
 * Self-leave: the current user removes themselves from the active tenant.
 * - If they're the only owner, the call is refused (would leave the workspace
 *   ownerless).
 * - On success the active-tenant cookie + prefs.activeTenant are cleared so
 *   the next request goes through fallback to another team or onboarding.
 */
export async function leaveWorkspaceAction(): Promise<MemberActionState> {
  let ctx;
  try {
    ctx = await requireTenant();
  } catch {
    return { ok: false, error: "Yetkiniz yok." };
  }

  try {
    const admin = createAdminClient();
    const memberships = await admin.teams.listMemberships(ctx.tenantId);
    const me = memberships.memberships.find((m) => m.userId === ctx.user.id);
    if (!me) return { ok: false, error: "Üyelik bulunamadı." };

    if (me.roles.includes("owner")) {
      const otherOwners = memberships.memberships.filter(
        (m) => m.userId !== ctx.user.id && m.roles.includes("owner"),
      );
      if (otherOwners.length === 0) {
        return {
          ok: false,
          error:
            "Tek sahip olduğunuz için ayrılamazsınız. Önce başka bir üyeyi sahip yapın.",
        };
      }
    }

    await admin.teams.deleteMembership(ctx.tenantId, me.$id);

    await logAudit({
      tenantId: ctx.tenantId,
      userId: ctx.user.id,
      action: "delete",
      entityType: "membership",
      entityId: me.$id,
      changes: { self: true, userEmail: ctx.user.email },
    });

    // Clear active-tenant pointers so the user lands somewhere safe next request.
    try {
      const session = await createSessionClient();
      const user = await session.account.get();
      const prefs = { ...(user.prefs as Record<string, unknown>) };
      delete prefs.activeTenant;
      await session.account.updatePrefs(prefs);
    } catch {
      /* ignore */
    }
    try {
      (await cookies()).delete(ACTIVE_TENANT_COOKIE);
    } catch {
      /* ignore */
    }
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

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

export async function updateMemberRoleAction(
  _prev: MemberActionState,
  formData: FormData,
): Promise<MemberActionState> {
  const membershipId = String(formData.get("membershipId") ?? "");
  const role = String(formData.get("role") ?? "");
  if (!membershipId || !["admin", "member"].includes(role)) {
    return { ok: false, error: "Geçersiz parametre." };
  }

  let ctx;
  try {
    ctx = await requireTenant();
    requireRole(ctx, ["owner"]);
  } catch {
    return { ok: false, error: "Sadece sahibi rol değiştirebilir." };
  }

  try {
    const { teams } = createAdminClient();
    await teams.updateMembership(ctx.tenantId, membershipId, [role]);

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

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

/**
 * Resolve a short invite code to its row. Used by /d/[code] (public-ish).
 * Returns minimal info; callers should not pass full row to client.
 */
export async function resolveInviteCode(code: string): Promise<InviteLink | null> {
  if (!code) return null;
  try {
    const { tablesDB } = createAdminClient();
    const result = await tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.inviteLinks,
      queries: [Query.equal("code", code), Query.limit(1)],
    });
    return (result.rows[0] as unknown as InviteLink) ?? null;
  } catch {
    return null;
  }
}

/**
 * Accept an invite for the currently logged-in user. Caller is responsible
 * for ensuring session.email matches invite.email (we double-check here too).
 */
export async function acceptInviteAction(code: string): Promise<MemberActionState> {
  if (!code) return { ok: false, error: "Geçersiz davet linki." };

  let user: Awaited<ReturnType<Awaited<ReturnType<typeof createSessionClient>>["account"]["get"]>>;
  try {
    const session = await createSessionClient();
    user = await session.account.get();
  } catch {
    return { ok: false, error: "Önce giriş yapmanız gerekiyor." };
  }

  const invite = await resolveInviteCode(code);
  if (!invite) return { ok: false, error: "Davet bulunamadı." };
  if (invite.status !== "pending") {
    return { ok: false, error: "Bu davet artık geçerli değil." };
  }
  if (invite.expiresAt && new Date(invite.expiresAt).getTime() < Date.now()) {
    return { ok: false, error: "Bu davetin süresi dolmuş." };
  }
  if (invite.email.toLowerCase() !== user.email.toLowerCase()) {
    return {
      ok: false,
      error: `Bu davet ${invite.email} adresine gönderilmiş; mevcut hesabınız (${user.email}) eşleşmiyor.`,
    };
  }

  const admin = createAdminClient();

  const activeTenantId = invite.tenantId;
  async function activateTenant() {
    try {
      const session = await createSessionClient();
      await session.account.updatePrefs({
        ...(user.prefs ?? {}),
        activeTenant: activeTenantId,
      });
    } catch {
      /* ignore */
    }
    try {
      (await cookies()).set(ACTIVE_TENANT_COOKIE, activeTenantId, {
        path: "/",
        httpOnly: true,
        sameSite: "strict",
        secure: process.env.NODE_ENV === "production",
        maxAge: 60 * 60 * 24 * 365,
      });
    } catch {
      /* ignore */
    }
  }

  // Already a member? Mark accepted, redirect.
  try {
    const memberships = await admin.teams.listMemberships(invite.tenantId);
    const existing = memberships.memberships.find((m) => m.userId === user.$id);
    if (existing) {
      await admin.tablesDB.updateRow(DATABASE_ID, TABLES.inviteLinks, invite.$id, {
        status: "accepted",
        acceptedAt: new Date().toISOString(),
        acceptedBy: user.$id,
      });
      await activateTenant();
      return { ok: true };
    }
  } catch {
    /* ignore */
  }

  try {
    await admin.teams.createMembership(
      invite.tenantId,
      [invite.role ?? "member"],
      undefined,
      user.$id,
    );

    await admin.tablesDB.updateRow(DATABASE_ID, TABLES.inviteLinks, invite.$id, {
      status: "accepted",
      acceptedAt: new Date().toISOString(),
      acceptedBy: user.$id,
    });

    await logAudit({
      tenantId: invite.tenantId,
      userId: user.$id,
      action: "create",
      entityType: "membership",
      entityId: invite.$id,
      changes: { via: "invite", code },
    });

    await activateTenant();
  } catch (e) {
    return { ok: false, error: appwriteError(e) };
  }

  return { ok: true };
}
