import { NextRequest, NextResponse } from "next/server";
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { createClient } from "@supabase/supabase-js";
import { cookies } from "next/headers";
import { randomUUID } from "crypto";
import { getS3SignedUrlIfExists } from "@/lib/aws-s3";

// Upstream (Lambda/API Gateway)
const UPSTREAM_BASE = process.env.STRUCTURE_API_BASE_URL || "https://0rryyjj6vh.execute-api.eu-west-3.amazonaws.com/default";
const PATH_PREFIX = process.env.STRUCTURE_API_PATH_PREFIX ?? "/api"; // ex: "/api" ou ""

function b64(s: string) {
  return Buffer.from(s, "utf8").toString("base64");
}

async function createSbServiceRole() {
  return createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL || "",
    process.env.SUPABASE_SERVICE_ROLE_KEY || ""
  );
}

async function assertStaff(sb: any, userId: string) {
  const { data: me } = await sb
    .from("staff_users")
    .select("is_staff")
    .eq("user_id", userId)
    .maybeSingle();
  return !!me?.is_staff;
}

/** Resolve l'organisation active (staff ou client) de la façon la plus simple possible. */
async function resolveOrganization(supabase: any, session: any) {
  const meta = session?.user?.user_metadata || {};
  const app = session?.user?.app_metadata || {};
  
  // Vérifier d'abord si c'est un utilisateur staff via la table staff_users
  const sbService = await createSbServiceRole();
  const isStaff = await assertStaff(sbService, session.user.id);

  if (isStaff) {
    const jar = cookies();
    const id = jar.get("active_org_id")?.value || null;
    const name = jar.get("active_org_name")?.value || null;
    // If staff has not selected an active org, allow global access by returning a null id.
    // Callers should treat `id === null` as "staff/global access" and avoid applying org filters.
    if (!id || !name) {
      return { id: null, name: "Staff Access", isStaff: true } as const;
    }
    return { id, name, isStaff: true } as const;
  }

  // Utilisateur client : récupérer son org via 2 requêtes simples
  const { data: member, error: mErr } = await supabase
    .from("organization_members")
    .select("org_id")
    .eq("user_id", session.user.id)
    .single();
  if (mErr || !member?.org_id) throw new Error("Aucune organisation associée à l'utilisateur.");

  const { data: org, error: oErr } = await supabase
    .from("organizations")
    .select("structure_api")
    .eq("id", member.org_id)
    .single();
  if (oErr || !org?.structure_api) throw new Error("Organisation introuvable.");

  return { id: member.org_id, name: org.structure_api, isStaff: false } as const;
}

export async function GET(req: NextRequest, { params }: { params: { id: string } }) {
  const requestId = randomUUID();
  try {
    const id = params?.id;
    if (!id) return NextResponse.json({ error: "missing_id" }, { status: 400 });

    const supabase = createRouteHandlerClient({ cookies });
    const { data: { session } } = await supabase.auth.getSession();
    if (!session) return NextResponse.json({ error: "unauthorized" }, { status: 401 });

    // Organisation
    const org = await resolveOrganization(supabase, session);

    // Choix du régime via query param, défaut = CDDU_MULTI
    const url = new URL(req.url);
    const regimeParam = (url.searchParams.get("regime") || "CDDU_MULTI").toUpperCase();
    const allowed = new Set(["CDDU_MULTI", "RG"]);
    const regime = allowed.has(regimeParam) ? regimeParam : "CDDU_MULTI";

    // If CDDU_MULTI or RG, read payslips from Supabase directly
    if (regime === "CDDU_MULTI" || regime === "RG") {
      // Helper slugify for organization name
      const slugify = (s: string) =>
        s
          .toLowerCase()
          .replace(/[^a-z0-9]+/g, "-")
          .replace(/(^-|-$)/g, "")
          .slice(0, 200);

      // Try to fetch the contract reference to build storage paths
      // When staff (org.isStaff === true), use the admin client to bypass RLS.
      let contractRow: any = null;
      let contractErr: any = null;
      if (org.isStaff) {
        const admin = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL || "", process.env.SUPABASE_SERVICE_ROLE_KEY || "");
        const r = await admin.from("cddu_contracts").select("contract_number").eq("id", id).maybeSingle();
        contractRow = r.data;
        contractErr = r.error;
      } else {
        let contractQuery: any = supabase.from("cddu_contracts").select("contract_number").eq("id", id);
        if (org.id) contractQuery = contractQuery.eq("org_id", org.id);
        const r = await contractQuery.maybeSingle();
        contractRow = r.data;
        contractErr = r.error;
      }

      if (contractErr) {
        // If Supabase fails, return an error so frontend can fallback
        return NextResponse.json({ error: contractErr.message, requestId }, { status: 500 });
      }

      const contractRef = contractRow?.contract_number || null;
      const orgSlug = slugify(org.name || "unknown");

      // Query payslips table for this contract
      // When staff (org.isStaff === true) use admin client to return all payslips for contract,
      // regardless of whether they have an active org selected or not.
      let pays: any = null;
      let paysErr: any = null;
      if (org.isStaff) {
        const admin = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL || "", process.env.SUPABASE_SERVICE_ROLE_KEY || "");
        const r = await admin.from("payslips").select("*").eq("contract_id", id).order("pay_number", { ascending: true });
        pays = r.data;
        paysErr = r.error;
      } else {
        let paysQuery: any = supabase.from("payslips").select("*").eq("contract_id", id);
        if (org.id) paysQuery = paysQuery.eq("organization_id", org.id);
        const r = await paysQuery.order("pay_number", { ascending: true });
        pays = r.data;
        paysErr = r.error;
      }

      if (paysErr) {
        return NextResponse.json({ error: paysErr.message }, { status: 500 });
      }

  const items = await Promise.all((pays || []).map(async (row: any) => {
        // Determine year/month from period_month or period_start
        const period = row.period_month || row.period_start || null;
        let year: number | null = null;
        let month: number | null = null;
        if (period) {
          const d = new Date(period);
          if (!isNaN(d.getTime())) {
            year = d.getFullYear();
            month = d.getMonth() + 1;
          }
        }

        // Use Supabase-provided storage_path or storage_url exclusively.
        // Prefer `row.storage_path` (S3 key) and presign it if it's not already a full URL.
        const storagePath = row.storage_path || null;
        let paieUrl: string | null = null;
        if (storagePath && !/^https?:\/\//.test(String(storagePath))) {
          try {
            const maybe = await getS3SignedUrlIfExists(storagePath);
            paieUrl = maybe ?? (row.storage_url || row.storage_path || null);
          } catch (e) {
            paieUrl = row.storage_url || row.storage_path || null;
          }
        } else {
          // If storage_path is already a URL or absent, prefer storage_url then storage_path
          paieUrl = row.storage_url || row.storage_path || null;
        }

        return {
          id: row.id,
          ordre: row.pay_number,
          mois: month,
          annee: year,
          aem_statut: row.aem_status || null,
          paie_pdf: paieUrl,
          // Expose net_amount as net_avant_pas and prefer net_after_withholding for net_a_payer
          net_avant_pas: row.net_amount || null,
          net_a_payer: row.net_after_withholding || row.net_amount || null,
          brut: row.gross_amount || null,
          cout_total: row.employer_cost || null,
          transfer_done: !!row.transfer_done,
          traite: row.processed ? "oui" : "non",
          // Expose raw period_start/period_end (from Supabase) as separate fields so the frontend
          // can format them reliably. Keep paie_traitee for backward compatibility.
          period_start: row.period_start || null,
          period_end: row.period_end || null,
          paie_traitee: row.period_start && row.period_end ? `${row.period_start} – ${row.period_end}` : null,
        };
        }));

      // Ensure ordered by ordre ascending
      items.sort((a: any, b: any) => (a.ordre ?? 0) - (b.ordre ?? 0));

      return NextResponse.json({ items, requestId }, { status: 200 });
    }

    const upstream = `${UPSTREAM_BASE}${PATH_PREFIX}/contrats/${encodeURIComponent(id)}/paies?regime=${encodeURIComponent(regime)}&_t=${Date.now()}`;

    // Prépare les headers minimalistes attendus par la Lambda
    const jar = cookies();
    const orgKey = jar.get("active_org_key")?.value || ""; // optionnel

    const headers = new Headers({
      Accept: "application/json",
      "x-forwarded-from": "odentas-espace-paie",
      "x-request-id": requestId,
    });
    // Only include organization-specific headers when an organization is selected.
    if (org.id) {
      headers.set("x-active-org-id", String(org.id));
      if (org.name) {
        headers.set("x-company-name", org.name);
        headers.set("x-company-name-b64", b64(org.name));
      }
    }
    if (orgKey) headers.set("x-company-key", orgKey);
    if (process.env.STRUCTURE_API_TOKEN) headers.set("authorization", `Bearer ${process.env.STRUCTURE_API_TOKEN}`);

    // Timeout 10s — simple et robuste
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(new Error("timeout")), 10_000);

    const res = await fetch(upstream, {
      method: "GET",
      headers: Object.fromEntries(headers.entries()),
      cache: "no-store",
      signal: controller.signal,
    });
    clearTimeout(timeout);

    // Normalisation : 404 upstream => items vides
    if (res.status === 404) return NextResponse.json({ items: [] }, { status: 200 });

    if (!res.ok) {
      const text = await res.text().catch(() => "");
      return NextResponse.json({ error: "upstream_error", status: res.status, body: text, requestId }, { status: 502 });
    }

    // Le backend peut renvoyer soit { items: [...] } soit directement [...]
    const data = await res.json().catch(() => null);
    const items = Array.isArray(data?.items) ? data.items : Array.isArray(data) ? data : [];

    // Tri de secours par "ordre" si présent, pour un affichage cohérent
    items.sort((a: any, b: any) => (a?.ordre ?? 1e9) - (b?.ordre ?? 1e9));

    return NextResponse.json({ items, requestId }, { status: 200 });
  } catch (err: any) {
    const msg = String(err?.message || err || "");
    if (msg.toLowerCase().includes("timeout") || err?.name === "AbortError") {
      return NextResponse.json({ error: "gateway_timeout", message: "Upstream timeout", requestId }, { status: 504 });
    }
    return NextResponse.json({ error: "server_error", message: msg, requestId }, { status: 500 });
  }
}
