import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
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",
};

class DocuSignConsentRequiredError extends Error {
  consentUrl: string;

  constructor(consentUrl: string) {
    super("DocuSign consent required");
    this.name = "DocuSignConsentRequiredError";
    this.consentUrl = consentUrl;
  }
}

function concatBytes(...arrays: Uint8Array[]) {
  const totalLength = arrays.reduce((sum, array) => sum + array.length, 0);
  const result = new Uint8Array(totalLength);
  let offset = 0;

  for (const array of arrays) {
    result.set(array, offset);
    offset += array.length;
  }

  return result;
}

function encodeDerLength(length: number) {
  if (length < 0x80) {
    return Uint8Array.of(length);
  }

  const bytes: number[] = [];
  let remaining = length;
  while (remaining > 0) {
    bytes.unshift(remaining & 0xff);
    remaining >>= 8;
  }

  return Uint8Array.of(0x80 | bytes.length, ...bytes);
}

function wrapPkcs1InPkcs8(pkcs1Der: Uint8Array) {
  const version = Uint8Array.of(0x02, 0x01, 0x00);
  const rsaEncryptionOid = Uint8Array.of(
    0x06, 0x09,
    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
  );
  const nullParams = Uint8Array.of(0x05, 0x00);

  const algorithmIdentifier = concatBytes(
    Uint8Array.of(0x30),
    encodeDerLength(rsaEncryptionOid.length + nullParams.length),
    rsaEncryptionOid,
    nullParams,
  );

  const privateKey = concatBytes(
    Uint8Array.of(0x04),
    encodeDerLength(pkcs1Der.length),
    pkcs1Der,
  );

  const body = concatBytes(version, algorithmIdentifier, privateKey);

  return concatBytes(
    Uint8Array.of(0x30),
    encodeDerLength(body.length),
    body,
  );
}

function normalizeSecretValue(rawValue: string) {
  return rawValue
    .trim()
    .replace(/^['"]/, "")
    .replace(/['"]$/, "")
    .replace(/\\r/g, "")
    .replace(/\\n/g, "\n")
    .trim();
}

function extractPemParts(rawValue: string) {
  const normalizedValue = normalizeSecretValue(rawValue);
  const pemMatch = normalizedValue.match(
    /-----BEGIN\s+([A-Z ]+PRIVATE KEY)-----([\s\S]*?)-----END\s+\1-----/
  );

  if (pemMatch) {
    return {
      pemType: pemMatch[1],
      pemBody: pemMatch[2].replace(/\s/g, ""),
    };
  }

  if (/^[A-Za-z0-9+/=\s]+$/.test(normalizedValue)) {
    return {
      pemType: "RAW_BASE64_PRIVATE_KEY",
      pemBody: normalizedValue.replace(/\s/g, ""),
    };
  }

  throw new Error("DocuSign private key secret must contain a valid PEM or base64 private key");
}

function getDocuSignConsentUrl() {
  const integrationKey = Deno.env.get("DOCUSIGN_INTEGRATION_KEY");
  const redirectUri =
    Deno.env.get("DOCUSIGN_REDIRECT_URI") ||
    "https://developers.docusign.com/platform/auth/consent";

  if (!integrationKey) {
    throw new Error("DOCUSIGN_INTEGRATION_KEY not configured");
  }

  const params = new URLSearchParams({
    response_type: "code",
    scope: "signature impersonation",
    client_id: integrationKey,
    redirect_uri: redirectUri,
  });

  return `https://account-d.docusign.com/oauth/auth?${params.toString()}`;
}

// DocuSign JWT Grant: get access token using RSA key
async function getDocuSignToken(): Promise<string> {
  const integrationKey = Deno.env.get("DOCUSIGN_INTEGRATION_KEY");
  const userId = Deno.env.get("DOCUSIGN_USER_ID");
  const rsaPrivateKey = Deno.env.get("DOCUSIGN_RSA_PRIVATE_KEY");

  if (!integrationKey || !userId || !rsaPrivateKey) {
    throw new Error("DocuSign credentials not configured");
  }

  const now = Math.floor(Date.now() / 1000);
  const header = { alg: "RS256", typ: "JWT" };
  const payload = {
    iss: integrationKey,
    sub: userId,
    aud: "account-d.docusign.com",
    iat: now,
    exp: now + 3600,
    scope: "signature impersonation",
  };

  const encode = (obj: unknown) =>
    btoa(JSON.stringify(obj))
      .replace(/\+/g, "-")
      .replace(/\//g, "_")
      .replace(/=+$/, "");

  const headerB64 = encode(header);
  const payloadB64 = encode(payload);
  const signingInput = `${headerB64}.${payloadB64}`;

  const { pemType, pemBody } = extractPemParts(rsaPrivateKey);
  const isEncryptedPem = pemType === "ENCRYPTED PRIVATE KEY";
  const isPkcs1Pem = pemType === "RSA PRIVATE KEY";

  if (isEncryptedPem) {
    throw new Error("DocuSign private key must be an unencrypted PEM key");
  }

  const { decode: decodeBase64 } = await import("https://deno.land/std@0.168.0/encoding/base64.ts");
  const rawKeyBytes = decodeBase64(pemBody);
  const keyBytes = isPkcs1Pem ? wrapPkcs1InPkcs8(rawKeyBytes) : rawKeyBytes;

  let cryptoKey: CryptoKey;
  try {
    cryptoKey = await crypto.subtle.importKey(
      "pkcs8",
      keyBytes,
      { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
      false,
      ["sign"]
    );
  } catch (primaryError) {
    if (isPkcs1Pem) {
      throw new Error(`Invalid DocuSign RSA private key: ${primaryError instanceof Error ? primaryError.message : String(primaryError)}`);
    }

    try {
      cryptoKey = await crypto.subtle.importKey(
        "pkcs8",
        wrapPkcs1InPkcs8(rawKeyBytes),
        { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
        false,
        ["sign"]
      );
    } catch {
      throw new Error(`Invalid DocuSign private key format: ${primaryError instanceof Error ? primaryError.message : String(primaryError)}`);
    }
  }

  const signature = await crypto.subtle.sign(
    "RSASSA-PKCS1-v1_5",
    cryptoKey,
    new TextEncoder().encode(signingInput)
  );

  const signatureB64 = btoa(String.fromCharCode(...new Uint8Array(signature)))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");

  const jwt = `${signingInput}.${signatureB64}`;

  // Exchange JWT for access token
  const tokenRes = await fetch("https://account-d.docusign.com/oauth/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`,
  });

  if (!tokenRes.ok) {
    const err = await tokenRes.text();
    if (err.includes("consent_required")) {
      throw new DocuSignConsentRequiredError(getDocuSignConsentUrl());
    }
    throw new Error(`DocuSign token error: ${err}`);
  }

  const tokenData = await tokenRes.json();
  return tokenData.access_token;
}

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

  try {
    // Validate user auth using getClaims for reliability
    const authHeader = req.headers.get("authorization");
    if (!authHeader?.startsWith("Bearer ")) throw new Error("Missing authorization header");

    const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
    const supabaseAnonKey = Deno.env.get("SUPABASE_ANON_KEY")!;
    const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;

    const authClient = createClient(supabaseUrl, supabaseAnonKey, {
      global: { headers: { Authorization: authHeader } },
    });

    const token = authHeader.replace("Bearer ", "");
    const { data: claimsData, error: claimsError } = await authClient.auth.getClaims(token);
    if (claimsError || !claimsData?.claims) throw new Error("Unauthorized");

    const userId = claimsData.claims.sub as string;

    const serviceClient = createClient(supabaseUrl, serviceRoleKey);

    // Check admin/manager role
    const { data: roles } = await serviceClient
      .from("user_roles")
      .select("role")
      .eq("user_id", userId);

    const hasAccess = roles?.some((r: any) =>
      ["admin", "manager"].includes(r.role)
    );
    if (!hasAccess) throw new Error("Insufficient permissions");

    const body = await req.json();
    const { action } = body;

    if (action === "send") {
      const {
        association_id,
        document_name,
        document_base64,
        file_extension,
        recipients,
        email_subject,
        email_body,
      } = body;

      if (!association_id || !document_name || !document_base64 || !recipients?.length) {
        throw new Error("Missing required fields: association_id, document_name, document_base64, recipients");
      }

      const accountId = Deno.env.get("DOCUSIGN_ACCOUNT_ID");
      if (!accountId) throw new Error("DOCUSIGN_ACCOUNT_ID not configured");

      const accessToken = await getDocuSignToken();

      // Build signers with tabs (signature + date fields)
      const signers = recipients.map((r: any, idx: number) => ({
        email: r.email,
        name: r.name,
        recipientId: String(idx + 1),
        routingOrder: String(idx + 1),
        tabs: {
          signHereTabs: [
            {
              anchorString: "/sig/",
              anchorXOffset: "0",
              anchorYOffset: "0",
              anchorUnits: "pixels",
            },
          ],
          dateSignedTabs: [
            {
              anchorString: "/date/",
              anchorXOffset: "0",
              anchorYOffset: "0",
              anchorUnits: "pixels",
            },
          ],
        },
      }));

      const envelopeDefinition = {
        emailSubject: email_subject || `Please sign: ${document_name}`,
        emailBlurb: email_body || "Please review and sign the attached document.",
        documents: [
          {
            documentBase64: document_base64,
            name: document_name,
            fileExtension: file_extension || "pdf",
            documentId: "1",
          },
        ],
        recipients: { signers },
        status: "sent",
      };

      const envelopeRes = await fetch(
        `https://demo.docusign.net/restapi/v2.1/accounts/${accountId}/envelopes`,
        {
          method: "POST",
          headers: {
            Authorization: `Bearer ${accessToken}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify(envelopeDefinition),
        }
      );

      if (!envelopeRes.ok) {
        const errText = await envelopeRes.text();
        throw new Error(`DocuSign API error [${envelopeRes.status}]: ${errText}`);
      }

      const envelopeData = await envelopeRes.json();

      // Record in database
      await serviceClient.from("docusign_envelopes").insert({
        association_id,
        envelope_id: envelopeData.envelopeId,
        document_name,
        status: "sent",
        recipients: JSON.stringify(recipients),
        sent_by: userId,
        sent_at: new Date().toISOString(),
      });

      return new Response(
        JSON.stringify({
          success: true,
          envelope_id: envelopeData.envelopeId,
          status: envelopeData.status,
        }),
        { headers: { ...corsHeaders, "Content-Type": "application/json" } }
      );
    }

    if (action === "status") {
      const { envelope_id } = body;
      if (!envelope_id) throw new Error("Missing envelope_id");

      const accountId = Deno.env.get("DOCUSIGN_ACCOUNT_ID");
      if (!accountId) throw new Error("DOCUSIGN_ACCOUNT_ID not configured");

      const accessToken = await getDocuSignToken();

      const statusRes = await fetch(
        `https://demo.docusign.net/restapi/v2.1/accounts/${accountId}/envelopes/${envelope_id}`,
        {
          headers: { Authorization: `Bearer ${accessToken}` },
        }
      );

      if (!statusRes.ok) {
        const errText = await statusRes.text();
        throw new Error(`DocuSign status error [${statusRes.status}]: ${errText}`);
      }

      const statusData = await statusRes.json();

      // Update local record
      await serviceClient
        .from("docusign_envelopes")
        .update({
          status: statusData.status,
          completed_at: statusData.status === "completed" ? new Date().toISOString() : null,
        })
        .eq("envelope_id", envelope_id);

      return new Response(JSON.stringify({ success: true, ...statusData }), {
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }

    throw new Error(`Unknown action: ${action}`);
  } catch (error: unknown) {
    console.error("DocuSign function error:", error);
    if (error instanceof DocuSignConsentRequiredError) {
      return new Response(JSON.stringify({
        success: false,
        error: error.message,
        code: "consent_required",
        consent_url: error.consentUrl,
      }), {
        status: 400,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }
    const message = error instanceof Error ? error.message : "Unknown error";
    return new Response(JSON.stringify({ success: false, error: message }), {
      status: 400,
      headers: { ...corsHeaders, "Content-Type": "application/json" },
    });
  }
});
