/**
 * Billing Service Layer
 * Reads always come from local Supabase cache.
 * Writes go to MoonInvoice API (if mooninvoice mode) + local cache.
 * BILLING_MODE env var controls the active data source.
 */

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
  { db: { schema: process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || 'bb' } }
);

export function getBillingMode(): 'mooninvoice' | 'native' {
  return (process.env.BILLING_MODE as 'mooninvoice' | 'native') || 'mooninvoice';
}

// ─── Companies ───────────────────────────────────────────────────────────────

export async function getCompanies() {
  const { data, error } = await supabase
    .from('billing_companies')
    .select('*')
    .eq('is_active', true)
    .order('name');
  if (error) throw error;
  return data;
}

export async function getCompanyById(id: string) {
  const { data, error } = await supabase
    .from('billing_companies')
    .select('*')
    .eq('id', id)
    .single();
  if (error) throw error;
  return data;
}

export async function updateCompany(id: string, updates: Record<string, any>) {
  const { data, error } = await supabase
    .from('billing_companies')
    .update({ ...updates, updated_at: new Date().toISOString() })
    .eq('id', id)
    .select()
    .single();
  if (error) throw error;
  return data;
}

// ─── Clients ─────────────────────────────────────────────────────────────────

export async function getClients(companyId?: string) {
  let query = supabase
    .from('billing_clients')
    .select('*, billing_companies(name, invoice_prefix)')
    .order('name');
  if (companyId) query = query.eq('company_id', companyId);
  const { data, error } = await query;
  if (error) throw error;
  return data;
}

export async function getClientById(id: string) {
  const { data, error } = await supabase
    .from('billing_clients')
    .select('*, billing_companies(name, invoice_prefix)')
    .eq('id', id)
    .single();
  if (error) throw error;
  return data;
}

export async function upsertClient(client: Record<string, any>) {
  const { data, error } = await supabase
    .from('billing_clients')
    .upsert({ ...client, updated_at: new Date().toISOString() })
    .select()
    .single();
  if (error) throw error;
  return data;
}

// ─── Invoices ────────────────────────────────────────────────────────────────

export async function getInvoices(filters: {
  companyId?: string;
  status?: string;
  clientId?: string;
  dateFrom?: string;
  dateTo?: string;
  search?: string;
}) {
  let query = supabase
    .from('billing_invoices')
    .select('*, billing_clients(name, organization), billing_companies(name, invoice_prefix)')
    .order('created_at', { ascending: false });

  if (filters.companyId) query = query.eq('company_id', filters.companyId);
  if (filters.status && filters.status !== 'all') query = query.eq('status', filters.status);
  if (filters.clientId) query = query.eq('client_id', filters.clientId);
  if (filters.dateFrom) query = query.gte('issue_date', filters.dateFrom);
  if (filters.dateTo) query = query.lte('issue_date', filters.dateTo);

  const { data, error } = await query;
  if (error) throw error;

  if (filters.search) {
    const s = filters.search.toLowerCase();
    return data?.filter(
      (inv: any) =>
        inv.invoice_number?.toLowerCase().includes(s) ||
        inv.billing_clients?.name?.toLowerCase().includes(s)
    );
  }

  return data;
}

export async function getInvoiceById(id: string) {
  const { data, error } = await supabase
    .from('billing_invoices')
    .select(
      '*, billing_clients(*), billing_companies(*), billing_line_items(*)'
    )
    .eq('id', id)
    .single();
  if (error) throw error;
  return data;
}

export async function upsertInvoice(invoice: Record<string, any>, lineItems?: Record<string, any>[]) {
  const { data, error } = await supabase
    .from('billing_invoices')
    .upsert({ ...invoice, updated_at: new Date().toISOString() })
    .select()
    .single();
  if (error) throw error;

  if (lineItems && data) {
    // Delete existing line items and re-insert
    await supabase.from('billing_line_items').delete().eq('invoice_id', data.id);
    if (lineItems.length > 0) {
      const { error: liError } = await supabase
        .from('billing_line_items')
        .insert(lineItems.map((li) => ({ ...li, invoice_id: data.id })));
      if (liError) throw liError;
    }
  }

  return data;
}

export async function updateInvoiceStatus(id: string, status: string) {
  const updates: Record<string, any> = { status, updated_at: new Date().toISOString() };
  if (status === 'paid') updates.paid_at = new Date().toISOString();
  const { data, error } = await supabase
    .from('billing_invoices')
    .update(updates)
    .eq('id', id)
    .select()
    .single();
  if (error) throw error;
  return data;
}

// ─── Payments ────────────────────────────────────────────────────────────────

export async function getPayments(companyId?: string) {
  let query = supabase
    .from('billing_payments')
    .select('*, billing_invoices(invoice_number, company_id), billing_clients(name)')
    .order('payment_date', { ascending: false });
  if (companyId) {
    query = query.eq('billing_invoices.company_id', companyId);
  }
  const { data, error } = await query;
  if (error) throw error;
  return data;
}

export async function insertPayment(payment: Record<string, any>) {
  const { data, error } = await supabase
    .from('billing_payments')
    .insert(payment)
    .select()
    .single();
  if (error) throw error;
  return data;
}

// ─── Dashboard stats ─────────────────────────────────────────────────────────

export async function getDashboardStats(companyId?: string) {
  const now = new Date();
  const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().split('T')[0];
  const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString().split('T')[0];
  const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0).toISOString().split('T')[0];
  const in7Days = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
  const today = now.toISOString().split('T')[0];

  let base = supabase.from('billing_invoices').select('*');
  if (companyId) base = base.eq('company_id', companyId);

  const { data: allInvoices } = await base;
  const invoices = allInvoices || [];

  const revenueThisMonth = invoices
    .filter((i: any) => i.status === 'paid' && i.paid_at >= startOfMonth)
    .reduce((s: number, i: any) => s + Number(i.total), 0);

  const revenueLastMonth = invoices
    .filter((i: any) => i.status === 'paid' && i.paid_at >= startOfLastMonth && i.paid_at <= endOfLastMonth)
    .reduce((s: number, i: any) => s + Number(i.total), 0);

  const outstanding = invoices.filter((i: any) => ['sent', 'viewed'].includes(i.status));
  const overdue = invoices.filter((i: any) => i.status === 'overdue' || (i.due_date < today && !['paid', 'cancelled', 'draft'].includes(i.status)));
  const paidThisMonth = invoices.filter((i: any) => i.status === 'paid' && i.paid_at >= startOfMonth);
  const upcoming = invoices.filter((i: any) => i.due_date >= today && i.due_date <= in7Days && !['paid', 'cancelled'].includes(i.status));

  return {
    revenueThisMonth,
    revenueLastMonth,
    revenueChange: revenueLastMonth > 0 ? ((revenueThisMonth - revenueLastMonth) / revenueLastMonth) * 100 : 0,
    outstandingCount: outstanding.length,
    outstandingAmount: outstanding.reduce((s: number, i: any) => s + Number(i.total) - Number(i.amount_paid), 0),
    overdueCount: overdue.length,
    overdueAmount: overdue.reduce((s: number, i: any) => s + Number(i.total) - Number(i.amount_paid), 0),
    paidThisMonthAmount: paidThisMonth.reduce((s: number, i: any) => s + Number(i.total), 0),
    upcomingCount: upcoming.length,
    upcomingAmount: upcoming.reduce((s: number, i: any) => s + Number(i.total) - Number(i.amount_paid), 0),
  };
}

export async function getMonthlyRevenue(companyId?: string, months = 12) {
  const result: { month: string; revenue: number }[] = [];
  const now = new Date();

  for (let i = months - 1; i >= 0; i--) {
    const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
    const start = d.toISOString().split('T')[0];
    const end = new Date(d.getFullYear(), d.getMonth() + 1, 0).toISOString().split('T')[0];

    let query = supabase
      .from('billing_invoices')
      .select('total')
      .eq('status', 'paid')
      .gte('paid_at', start)
      .lte('paid_at', end);
    if (companyId) query = query.eq('company_id', companyId);

    const { data } = await query;
    const revenue = (data || []).reduce((s: number, r: any) => s + Number(r.total), 0);
    result.push({
      month: d.toLocaleString('default', { month: 'short', year: '2-digit' }),
      revenue,
    });
  }

  return result;
}

// ─── Billing settings ────────────────────────────────────────────────────────

export async function getBillingSettings() {
  const { data, error } = await supabase
    .from('billing_settings')
    .select('*')
    .limit(1)
    .single();
  if (error && error.code !== 'PGRST116') throw error;
  return data;
}

export async function updateBillingSettings(updates: Record<string, any>) {
  const existing = await getBillingSettings();
  if (existing) {
    const { data, error } = await supabase
      .from('billing_settings')
      .update({ ...updates, updated_at: new Date().toISOString() })
      .eq('id', existing.id)
      .select()
      .single();
    if (error) throw error;
    return data;
  } else {
    const { data, error } = await supabase
      .from('billing_settings')
      .insert(updates)
      .select()
      .single();
    if (error) throw error;
    return data;
  }
}

// ─── Sync log ────────────────────────────────────────────────────────────────

export async function logSync(entry: {
  sync_type: string;
  company_id?: string;
  records_synced?: number;
  errors?: any;
  status: 'running' | 'completed' | 'failed';
  started_at?: string;
  completed_at?: string;
}) {
  const { data, error } = await supabase
    .from('billing_sync_log')
    .insert(entry)
    .select()
    .single();
  if (error) throw error;
  return data;
}
