import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.49.1";

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 QBO_BASE_URL = "https://quickbooks.api.intuit.com";

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

  try {
    const authHeader = req.headers.get("authorization");
    if (!authHeader) {
      return new Response(JSON.stringify({ error: "Unauthorized" }), {
        status: 401,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }

    const QBO_CLIENT_ID = Deno.env.get("QBO_CLIENT_ID");
    const QBO_CLIENT_SECRET = Deno.env.get("QBO_CLIENT_SECRET");
    const QBO_REALM_ID = Deno.env.get("QBO_REALM_ID");
    const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!;
    const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;

    if (!QBO_CLIENT_ID || !QBO_CLIENT_SECRET || !QBO_REALM_ID) {
      return new Response(JSON.stringify({ error: "QBO credentials not configured" }), {
        status: 500,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }

    const supabaseAdmin = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);

    // Verify the user is an admin
    const token = authHeader.replace("Bearer ", "");
    const { data: { user }, error: authError } = await createClient(SUPABASE_URL, Deno.env.get("SUPABASE_ANON_KEY")!, {
      global: { headers: { Authorization: `Bearer ${token}` } },
    }).auth.getUser();

    if (authError || !user) {
      return new Response(JSON.stringify({ error: "Unauthorized" }), {
        status: 401,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }

    const { data: roles } = await supabaseAdmin.from("user_roles").select("role").eq("user_id", user.id);
    const isAdmin = roles?.some((r: any) => r.role === "admin");
    if (!isAdmin) {
      return new Response(JSON.stringify({ error: "Forbidden: admin only" }), {
        status: 403,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }

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

    // Get or refresh QBO access token
    const accessToken = await getQBOAccessToken(supabaseAdmin, QBO_CLIENT_ID, QBO_CLIENT_SECRET);
    if (!accessToken) {
      return new Response(JSON.stringify({
        error: "QBO authentication failed. You may need to re-authorize.",
        reconnectRequired: true,
      }), {
        status: 200,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }

    let result: any = { message: "Unknown action" };

    switch (action) {
      case "sync_all": {
        // Pull invoices from QBO
        const invoices = await fetchQBOData(accessToken, QBO_REALM_ID, "Invoice");
        const payments = await fetchQBOData(accessToken, QBO_REALM_ID, "Payment");
        const purchases = await fetchQBOData(accessToken, QBO_REALM_ID, "Purchase");

        let synced = 0;

        // Sync invoices as credits (money owed to us)
        if (invoices?.QueryResponse?.Invoice) {
          for (const inv of invoices.QueryResponse.Invoice) {
            const existing = await supabaseAdmin.from("company_bank_transactions")
              .select("id").eq("qbo_id", `INV-${inv.Id}`).maybeSingle();

            if (!existing.data) {
              await supabaseAdmin.from("company_bank_transactions").insert({
                date: inv.TxnDate,
                transaction_type: "invoice_payment",
                description: `QBO Invoice #${inv.DocNumber || inv.Id} - ${inv.CustomerRef?.name || "Customer"}`,
                reference_number: inv.DocNumber || null,
                credit: inv.TotalAmt || 0,
                debit: 0,
                category: "QBO Invoice",
                qbo_id: `INV-${inv.Id}`,
                qbo_sync_status: "synced",
              });
              synced++;
            }
          }
        }

        // Sync payments as credits
        if (payments?.QueryResponse?.Payment) {
          for (const pmt of payments.QueryResponse.Payment) {
            const existing = await supabaseAdmin.from("company_bank_transactions")
              .select("id").eq("qbo_id", `PMT-${pmt.Id}`).maybeSingle();

            if (!existing.data) {
              await supabaseAdmin.from("company_bank_transactions").insert({
                date: pmt.TxnDate,
                transaction_type: "deposit",
                description: `QBO Payment - ${pmt.CustomerRef?.name || "Customer"}`,
                reference_number: pmt.PaymentRefNum || null,
                credit: pmt.TotalAmt || 0,
                debit: 0,
                category: "QBO Payment",
                qbo_id: `PMT-${pmt.Id}`,
                qbo_sync_status: "synced",
              });
              synced++;
            }
          }
        }

        // Sync purchases as debits
        if (purchases?.QueryResponse?.Purchase) {
          for (const pur of purchases.QueryResponse.Purchase) {
            const existing = await supabaseAdmin.from("company_bank_transactions")
              .select("id").eq("qbo_id", `PUR-${pur.Id}`).maybeSingle();

            if (!existing.data) {
              await supabaseAdmin.from("company_bank_transactions").insert({
                date: pur.TxnDate,
                transaction_type: "payment",
                description: `QBO Purchase - ${pur.EntityRef?.name || "Vendor"}`,
                reference_number: pur.DocNumber || null,
                debit: pur.TotalAmt || 0,
                credit: 0,
                category: "QBO Purchase",
                qbo_id: `PUR-${pur.Id}`,
                qbo_sync_status: "synced",
              });
              synced++;
            }
          }
        }

        // Push unsynced local transactions to QBO
        const { data: unsynced } = await supabaseAdmin.from("company_bank_transactions")
          .select("*")
          .is("qbo_id", null)
          .eq("qbo_sync_status", "pending");

        let pushed = 0;
        if (unsynced) {
          for (const txn of unsynced) {
            try {
              const qboResult = await pushToQBO(accessToken, QBO_REALM_ID, txn);
              if (qboResult?.Id) {
                await supabaseAdmin.from("company_bank_transactions")
                  .update({ qbo_id: `LOCAL-${qboResult.Id}`, qbo_sync_status: "synced" })
                  .eq("id", txn.id);
                pushed++;
              }
            } catch (e) {
              console.error("Push to QBO failed for txn:", txn.id, e);
            }
          }
        }

        // Log the sync
        await supabaseAdmin.from("qbo_sync_log").insert({
          sync_type: "full_sync",
          direction: "bidirectional",
          entity_type: "all",
          status: "completed",
          payload: { invoices_pulled: synced, transactions_pushed: pushed },
        });

        result = {
          message: `Sync complete: ${synced} transactions pulled from QBO, ${pushed} pushed to QBO`,
          pulled: synced,
          pushed,
        };
        break;
      }

      case "test_connection": {
        const companyInfo = await fetchQBOData(accessToken, QBO_REALM_ID, "CompanyInfo");
        result = {
          message: "Connection successful",
          company: companyInfo?.QueryResponse?.CompanyInfo?.[0]?.CompanyName || "Connected",
        };
        break;
      }

      default:
        result = { error: `Unknown action: ${action}` };
    }

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

  } catch (err) {
    console.error("QBO sync error:", err);
    return new Response(JSON.stringify({ error: err instanceof Error ? err.message : "Unknown error" }), {
      status: 500,
      headers: { ...corsHeaders, "Content-Type": "application/json" },
    });
  }
});

async function getQBOAccessToken(
  supabaseAdmin: any,
  clientId: string,
  clientSecret: string,
): Promise<string | null> {
  // Check for stored refresh token
  const { data: settings } = await supabaseAdmin
    .from("company_settings")
    .select("*")
    .in("key", ["qbo_access_token", "qbo_refresh_token", "qbo_token_expiry"]);

  const tokenMap: Record<string, string> = {};
  settings?.forEach((s: any) => { tokenMap[s.key] = s.value; });

  const now = Date.now();
  
  // If we have a valid access token, use it
  if (tokenMap.qbo_access_token && tokenMap.qbo_token_expiry && parseInt(tokenMap.qbo_token_expiry) > now) {
    return tokenMap.qbo_access_token;
  }

  // Try to refresh
  if (tokenMap.qbo_refresh_token) {
    try {
      const resp = await fetch("https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer", {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
          "Authorization": `Basic ${btoa(`${clientId}:${clientSecret}`)}`,
        },
        body: new URLSearchParams({
          grant_type: "refresh_token",
          refresh_token: tokenMap.qbo_refresh_token,
        }),
      });

      if (resp.ok) {
        const tokens = await resp.json();
        const expiry = (now + tokens.expires_in * 1000).toString();

        // Upsert tokens
        for (const [key, value] of Object.entries({
          qbo_access_token: tokens.access_token,
          qbo_refresh_token: tokens.refresh_token,
          qbo_token_expiry: expiry,
        })) {
          const { data: existing } = await supabaseAdmin.from("company_settings").select("id").eq("key", key).maybeSingle();
          if (existing) {
            await supabaseAdmin.from("company_settings").update({ value }).eq("key", key);
          } else {
            await supabaseAdmin.from("company_settings").insert({ key, value });
          }
        }

        return tokens.access_token;
      }
    } catch (e) {
      console.error("Token refresh failed:", e);
    }
  }

  return null;
}

async function fetchQBOData(accessToken: string, realmId: string, entity: string): Promise<any> {
  const query = `SELECT * FROM ${entity} MAXRESULTS 1000`;
  const resp = await fetch(
    `${QBO_BASE_URL}/v3/company/${realmId}/query?query=${encodeURIComponent(query)}`,
    {
      headers: {
        "Authorization": `Bearer ${accessToken}`,
        "Accept": "application/json",
        "Content-Type": "application/json",
      },
    },
  );

  if (!resp.ok) {
    const text = await resp.text();
    console.error(`QBO query failed for ${entity} [${resp.status}]:`, text);
    return null;
  }

  return resp.json();
}

async function pushToQBO(accessToken: string, realmId: string, txn: any): Promise<any> {
  // Push as a JournalEntry to QBO
  const entry = {
    Line: [
      {
        DetailType: "JournalEntryLineDetail",
        Amount: txn.debit > 0 ? txn.debit : txn.credit,
        JournalEntryLineDetail: {
          PostingType: txn.debit > 0 ? "Debit" : "Credit",
        },
        Description: txn.description || "Synced from ACM",
      },
    ],
    TxnDate: txn.date,
  };

  const resp = await fetch(
    `${QBO_BASE_URL}/v3/company/${realmId}/journalentry?minorversion=73`,
    {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${accessToken}`,
        "Accept": "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify(entry),
    },
  );

  if (!resp.ok) {
    const text = await resp.text();
    throw new Error(`QBO push failed [${resp.status}]: ${text}`);
  }

  const result = await resp.json();
  return result.JournalEntry;
}
