import { createClient } from "https://esm.sh/@supabase/supabase-js@2";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version",
};

const STAFF_ROLES = ["admin", "manager"] as const;

function jsonResponse(body: unknown, status = 200) {
  return new Response(JSON.stringify(body), { status, headers: { ...corsHeaders, "Content-Type": "application/json" } });
}

function quote(value: string) {
  return `"${String(value ?? "").replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
}

async function getAuthorizedClient(req: Request) {
  const authHeader = req.headers.get("authorization") || "";
  if (!authHeader.startsWith("Bearer ")) return { error: jsonResponse({ success: false, error: "Unauthorized" }, 401) };

  const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
  const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!;
  const client = createClient(supabaseUrl, anonKey, { global: { headers: { Authorization: authHeader } } });
  const token = authHeader.replace("Bearer ", "");
  const { data: claimsData, error } = await client.auth.getClaims(token);
  const callerId = claimsData?.claims?.sub;
  if (error || !callerId) return { error: jsonResponse({ success: false, error: "Unauthorized" }, 401) };

  for (const role of STAFF_ROLES) {
    const { data: hasRole } = await client.rpc("has_role", { _user_id: callerId, _role: role });
    if (hasRole) return { client, callerId };
  }
  return { error: jsonResponse({ success: false, error: "Insufficient permissions" }, 403) };
}

async function readUntil(reader: ReadableStreamDefaultReader<Uint8Array>, tag: string, timeoutMs = 20000) {
  const decoder = new TextDecoder();
  let text = "";
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const { value, done } = await reader.read();
    if (done) break;
    text += decoder.decode(value, { stream: true });
    if (text.includes(`${tag} OK`) || text.includes(`${tag} NO`) || text.includes(`${tag} BAD`)) break;
  }
  return text;
}

async function writeCommand(writer: WritableStreamDefaultWriter<Uint8Array>, tag: string, command: string) {
  await writer.write(new TextEncoder().encode(`${tag} ${command}\r\n`));
}

function parseHeaderBlock(block: string) {
  const get = (name: string) => {
    const match = block.match(new RegExp(`^${name}:\\s*([\\s\\S]*?)(?=\\r?\\n[A-Za-z-]+:|$)`, "im"));
    return (match?.[1] || "").replace(/\r?\n\s+/g, " ").trim();
  };
  const uid = block.match(/UID\s+(\d+)/i)?.[1] || crypto.randomUUID();
  const flags = block.match(/FLAGS\s+\(([^)]*)\)/i)?.[1] || "";
  return {
    id: uid,
    from: get("From") || "Unknown sender",
    subject: get("Subject") || "(no subject)",
    date: get("Date"),
    unread: !/\\Seen/i.test(flags),
  };
}

function parseMessages(fetchResponse: string) {
  return fetchResponse
    .split(/\r?\n\)\r?\n(?=\* \d+ FETCH|[A-Z]\d+ OK|$)/g)
    .filter((block) => /FETCH/i.test(block))
    .map(parseHeaderBlock)
    .filter((message) => message.from || message.subject)
    .reverse();
}

function filterMessagesByOpenedAt(messages: ReturnType<typeof parseMessages>, openedSince?: string) {
  if (!openedSince) return messages;
  const openedAt = new Date(openedSince).getTime();
  if (!Number.isFinite(openedAt)) return messages;
  return messages.filter((message) => {
    const receivedAt = new Date(message.date).getTime();
    return Number.isFinite(receivedAt) && receivedAt >= openedAt;
  });
}

async function fetchImapMessages(config: any, openedSince?: string) {
  const conn = config.use_tls
    ? await Deno.connectTls({ hostname: config.imap_host, port: Number(config.imap_port || 993) })
    : await Deno.connect({ hostname: config.imap_host, port: Number(config.imap_port || 143) });

  const reader = conn.readable.getReader();
  const writer = conn.writable.getWriter();
  await readUntil(reader, "*");

  await writeCommand(writer, "A1", `LOGIN ${quote(config.imap_username)} ${quote(config.imap_password)}`);
  const login = await readUntil(reader, "A1");
  if (!login.includes("A1 OK")) throw new Error("IMAP login failed. Check username, password, host, and port.");

  await writeCommand(writer, "A2", "SELECT INBOX");
  const selected = await readUntil(reader, "A2");
  const exists = Number(selected.match(/\*\s+(\d+)\s+EXISTS/i)?.[1] || 0);
  if (exists === 0) {
    await writeCommand(writer, "A4", "LOGOUT");
    await readUntil(reader, "A4", 3000).catch(() => "");
    conn.close();
    return [];
  }

  const start = Math.max(1, exists - 49);
  await writeCommand(writer, "A3", `FETCH ${start}:${exists} (UID FLAGS BODY.PEEK[HEADER.FIELDS (FROM SUBJECT DATE)])`);
  const fetched = await readUntil(reader, "A3");
  await writeCommand(writer, "A4", "LOGOUT");
  await readUntil(reader, "A4", 3000).catch(() => "");
  conn.close();
  return filterMessagesByOpenedAt(parseMessages(fetched), openedSince);
}

Deno.serve(async (req) => {
  if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });

  try {
    const auth = await getAuthorizedClient(req);
    if (auth.error) return auth.error;
    const { client, callerId } = auth;
    const body = await req.json().catch(() => ({}));
    const action = String(body.action || "fetch_messages");

    if (action === "list_configs") {
      const { data, error } = await client.from("email_inbox_configs").select("id, display_name, email_address, imap_host, imap_port, imap_username, use_tls, is_active, created_at").order("created_at", { ascending: false });
      if (error) throw error;
      return jsonResponse({ success: true, configs: data || [] });
    }

    if (action === "save_config") {
      const config = body.config || {};
      if (!config.display_name || !config.email_address || !config.imap_host || !config.imap_username || !config.imap_password) {
        return jsonResponse({ success: false, error: "Display name, email, IMAP host, username, and password are required." }, 400);
      }
      const payload = {
        user_id: callerId,
        display_name: String(config.display_name),
        email_address: String(config.email_address).toLowerCase(),
        imap_host: String(config.imap_host),
        imap_port: Number(config.imap_port || 993),
        imap_username: String(config.imap_username),
        imap_password: String(config.imap_password),
        use_tls: config.use_tls !== false,
        is_active: config.is_active !== false,
      };
      const query = config.id
        ? client.from("email_inbox_configs").update(payload).eq("id", config.id).select("id").single()
        : client.from("email_inbox_configs").insert(payload).select("id").single();
      const { data, error } = await query;
      if (error) throw error;
      return jsonResponse({ success: true, id: data.id });
    }

    if (action === "delete_config") {
      if (!body.id) return jsonResponse({ success: false, error: "Inbox config id is required." }, 400);
      const { error } = await client.from("email_inbox_configs").delete().eq("id", body.id);
      if (error) throw error;
      return jsonResponse({ success: true });
    }

    const configId = body.config_id;
    const query = client.from("email_inbox_configs").select("*").eq("is_active", true).order("created_at", { ascending: false }).limit(1);
    const { data: configs, error } = configId ? await client.from("email_inbox_configs").select("*").eq("id", configId).limit(1) : await query;
    if (error) throw error;
    const config = configs?.[0];
    if (!config) return jsonResponse({ success: true, messages: [], configs: [] });
    const messages = await fetchImapMessages(config, typeof body.opened_since === "string" ? body.opened_since : undefined);
    return jsonResponse({ success: true, messages, mailbox: { id: config.id, display_name: config.display_name, email_address: config.email_address } });
  } catch (error) {
    console.error("[fetch-imap-inbox]", error);
    return jsonResponse({ success: false, error: error instanceof Error ? error.message : "Failed to fetch inbox" }, 500);
  }
});