export interface PaymentEntryReference {
  name?: string;
  reference_doctype?: string;
  reference_name?: string;
  due_date?: string;
  total_amount?: number;
  outstanding_amount?: number;
  allocated_amount?: number;
  exchange_rate?: number;
  exchange_gain_loss?: number;
  account?: string;
  payment_term_outstanding?: number;
  idx?: number;
  [key: string]: any;
}

export interface PaymentEntry {
  name?: string;
  payment_type?: string;
  posting_date?: string;
  company?: string;
  mode_of_payment?: string;
  party_type?: string;
  party?: string;
  party_name?: string;
  paid_from?: string;
  paid_from_account_type?: string;
  paid_from_account_currency?: string;
  paid_from_account_balance?: number;
  paid_to?: string;
  paid_to_account_type?: string;
  paid_to_account_currency?: string;
  paid_to_account_balance?: number;
  paid_amount?: number;
  received_amount?: number;
  source_exchange_rate?: number;
  target_exchange_rate?: number;
  base_paid_amount?: number;
  base_received_amount?: number;
  total_allocated_amount?: number;
  unallocated_amount?: number;
  difference_amount?: number;
  project?: string;
  cost_center?: string;
  status?: string;
  docstatus?: number;
  remarks?: string;
  /** Cheque / Reference No (Transaction ID) */
  reference_no?: string;
  /** Cheque / Reference Date */
  reference_date?: string;
  references?: PaymentEntryReference[];
  [key: string]: any;
}

class PaymentEntryService {
  private csrfToken: string | null = null;

  private async getCSRFToken(): Promise<string | null> {
    if (typeof window === 'undefined') return null;
    if ((window as any).csrf_token) return (window as any).csrf_token;
    if ((window as any).frappe?.csrf_token) return (window as any).frappe.csrf_token;
    try {
      const res = await fetch('/api/method/frappe.sessions.get_csrf_token', { credentials: 'include' });
      if (res.ok) {
        const json = await res.json();
        if (json.message) { (window as any).csrf_token = json.message; return json.message; }
      }
    } catch { /* ignore */ }
    return null;
  }

  private async getHeaders(): Promise<Record<string, string>> {
    const h: Record<string, string> = { 'Content-Type': 'application/json', Accept: 'application/json' };
    const csrf = await this.getCSRFToken();
    if (csrf) h['X-Frappe-CSRF-Token'] = csrf;
    return h;
  }

  private async fetchJson(url: string, opts: RequestInit = {}): Promise<any> {
    const r = await fetch(url, { credentials: 'include', ...opts });
    const body = await r.json();
    if (!r.ok) {
      let msg = body?.exc_type || body?.message || `HTTP ${r.status}`;
      if (body?._server_messages) {
        try {
          const msgs = JSON.parse(body._server_messages);
          const parsed = Array.isArray(msgs) ? msgs.map((m: string) => { try { return JSON.parse(m).message; } catch { return m; } }) : [];
          if (parsed.length) msg = parsed.join('\n');
        } catch { /* ignore */ }
      }
      throw new Error(msg);
    }
    return body;
  }

  async getPaymentEntries(params: { filters?: any[]; limit_start?: number; limit_page_length?: number } = {}): Promise<PaymentEntry[]> {
    const q = new URLSearchParams();
    const fields = ['name', 'payment_type', 'posting_date', 'party', 'party_name', 'party_type', 'paid_amount', 'received_amount', 'status', 'mode_of_payment', 'company', 'docstatus', 'creation'];
    q.set('fields', JSON.stringify(fields));
    if (params.filters?.length) q.set('filters', JSON.stringify(params.filters));
    q.set('limit_start', String(params.limit_start ?? 0));
    q.set('limit_page_length', String(params.limit_page_length ?? 20));
    q.set('order_by', 'creation desc');
    const body = await this.fetchJson(`/api/resource/Payment Entry?${q}`);
    return body.data || [];
  }

  async getPaymentEntryCount(filters: any[] = []): Promise<number> {
    const q = new URLSearchParams();
    q.set('fields', JSON.stringify(['count(name) as count']));
    if (filters.length) q.set('filters', JSON.stringify(filters));
    try {
      const body = await this.fetchJson(`/api/resource/Payment%20Entry?${q}`);
      return body.data?.[0]?.count ?? 0;
    } catch {
      return 0;
    }
  }

  async getPaymentEntry(name: string): Promise<PaymentEntry> {
    const body = await this.fetchJson(`/api/resource/Payment Entry/${encodeURIComponent(name)}`);
    return body.data;
  }

  async createPaymentEntry(data: Partial<PaymentEntry>): Promise<PaymentEntry> {
    const headers = await this.getHeaders();
    const body = await this.fetchJson('/api/resource/Payment Entry', { method: 'POST', headers, body: JSON.stringify({ ...data, doctype: 'Payment Entry' }) });
    return body.data;
  }

  async updatePaymentEntry(name: string, data: Partial<PaymentEntry>): Promise<PaymentEntry> {
    const headers = await this.getHeaders();
    const body = await this.fetchJson(`/api/resource/Payment Entry/${encodeURIComponent(name)}`, { method: 'PUT', headers, body: JSON.stringify(data) });
    return body.data;
  }

  async submitPaymentEntry(name: string): Promise<PaymentEntry> {
    const headers = await this.getHeaders();
    const body = await this.fetchJson(`/api/resource/Payment Entry/${encodeURIComponent(name)}`, { method: 'PUT', headers, body: JSON.stringify({ docstatus: 1 }) });
    return body.data;
  }
}

export const paymentEntryService = new PaymentEntryService();
