import type { AccountingProvider, OAuthTokens, IncomeEntry, ExpenseEntry } from "./types"
import { redirectUri } from "./types"

// Xero. Docs: https://developer.xero.com/documentation/guides/oauth2/
const CLIENT_ID = process.env.XERO_CLIENT_ID ?? ""
const CLIENT_SECRET = process.env.XERO_CLIENT_SECRET ?? ""
const TOKEN_URL = "https://identity.xero.com/connect/token"
const API_BASE = "https://api.xero.com/api.xro/2.0"
const SCOPES = "openid profile email accounting.transactions accounting.contacts offline_access"
// Standard default account codes (present in most Xero orgs).
const SALES_CODE = process.env.XERO_SALES_ACCOUNT_CODE ?? "200"
const EXPENSE_CODE = process.env.XERO_EXPENSE_ACCOUNT_CODE ?? "400"

function basicAuth() {
  return "Basic " + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64")
}

async function tokenRequest(form: Record<string, string>): Promise<OAuthTokens> {
  const res = await fetch(TOKEN_URL, {
    method: "POST",
    headers: { Authorization: basicAuth(), "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams(form),
  })
  if (!res.ok) throw new Error(`Xero token error ${res.status}: ${await res.text()}`)
  const j = (await res.json()) as { access_token: string; refresh_token: string; expires_in: number }
  return {
    accessToken: j.access_token,
    refreshToken: j.refresh_token,
    expiresAt: new Date(Date.now() + j.expires_in * 1000).toISOString(),
    realmId: null,
    orgName: null,
  }
}

async function api(tokens: OAuthTokens, path: string, init?: RequestInit) {
  const res = await fetch(`${API_BASE}/${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${tokens.accessToken}`,
      "Xero-tenant-id": tokens.realmId ?? "",
      Accept: "application/json",
      "Content-Type": "application/json",
      ...(init?.headers ?? {}),
    },
  })
  if (!res.ok) throw new Error(`Xero API ${res.status}: ${(await res.text()).slice(0, 300)}`)
  return res.json()
}

export const xero: AccountingProvider = {
  id: "xero",
  label: "Xero",
  configured: () => Boolean(CLIENT_ID && CLIENT_SECRET),

  getAuthUrl(state) {
    const p = new URLSearchParams({
      response_type: "code",
      client_id: CLIENT_ID,
      redirect_uri: redirectUri("xero"),
      scope: SCOPES,
      state,
    })
    return `https://login.xero.com/identity/connect/authorize?${p.toString()}`
  },

  async exchangeCode(code) {
    const tokens = await tokenRequest({ grant_type: "authorization_code", code, redirect_uri: redirectUri("xero") })
    // Resolve the connected organisation (tenant).
    const res = await fetch("https://api.xero.com/connections", { headers: { Authorization: `Bearer ${tokens.accessToken}`, Accept: "application/json" } })
    const conns = (await res.json()) as Array<{ tenantId: string; tenantName: string }>
    tokens.realmId = conns[0]?.tenantId ?? null
    tokens.orgName = conns[0]?.tenantName ?? null
    return tokens
  },

  async refresh(tokens) {
    const next = await tokenRequest({ grant_type: "refresh_token", refresh_token: tokens.refreshToken })
    next.realmId = tokens.realmId
    next.orgName = tokens.orgName
    return next
  },

  async pushIncome(tokens, entries) {
    if (!entries.length) return { synced: 0 }
    // ACCREC invoices (sales) against the standard sales account.
    const invoices = entries.map((e) => ({
      Type: "ACCREC",
      Contact: { Name: e.customerName },
      Date: e.date,
      DueDate: e.date,
      Status: "AUTHORISED",
      Reference: `pmn:${e.externalId}`,
      LineItems: [{ Description: e.description, Quantity: 1, UnitAmount: e.amount, AccountCode: SALES_CODE }],
    }))
    await api(tokens, "Invoices", { method: "POST", body: JSON.stringify({ Invoices: invoices }) })
    return { synced: entries.length }
  },

  async pushExpense(tokens, entries) {
    if (!entries.length) return { synced: 0 }
    // ACCPAY invoices (bills) against the standard expense account.
    const invoices = entries.map((e) => ({
      Type: "ACCPAY",
      Contact: { Name: e.vendor || "Expense" },
      Date: e.date,
      DueDate: e.date,
      Status: "AUTHORISED",
      Reference: `pmn:${e.externalId}`,
      LineItems: [{ Description: e.description, Quantity: 1, UnitAmount: e.amount, AccountCode: EXPENSE_CODE }],
    }))
    await api(tokens, "Invoices", { method: "POST", body: JSON.stringify({ Invoices: invoices }) })
    return { synced: entries.length }
  },
}
