package models

import (
	"database/sql"
	"fmt"
	"time"
)

type Invoice struct {
	ID            int
	OrderID       sql.NullInt64
	CustomerID    int
	CustomerName  string
	InvoiceNumber string
	Status        string
	Amount        float64
	DueDate       string
	PaidDate      sql.NullString
	CreatedAt     time.Time
}

func InvoiceGetAll(db *sql.DB, status string) ([]Invoice, error) {
	query := `SELECT i.id, i.order_id, i.customer_id, c.name, i.invoice_number, i.status, i.amount, i.due_date, i.paid_date, i.created_at
		FROM invoices i JOIN customers c ON i.customer_id = c.id`
	args := []interface{}{}
	if status != "" {
		query += " WHERE i.status = ?"
		args = append(args, status)
	}
	query += " ORDER BY i.created_at DESC"

	rows, err := db.Query(query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var invoices []Invoice
	for rows.Next() {
		var inv Invoice
		if err := rows.Scan(&inv.ID, &inv.OrderID, &inv.CustomerID, &inv.CustomerName, &inv.InvoiceNumber, &inv.Status, &inv.Amount, &inv.DueDate, &inv.PaidDate, &inv.CreatedAt); err != nil {
			return nil, err
		}
		invoices = append(invoices, inv)
	}
	return invoices, nil
}

func InvoiceGetByID(db *sql.DB, id int) (*Invoice, error) {
	inv := &Invoice{}
	err := db.QueryRow(
		`SELECT i.id, i.order_id, i.customer_id, c.name, i.invoice_number, i.status, i.amount, i.due_date, i.paid_date, i.created_at
		FROM invoices i JOIN customers c ON i.customer_id = c.id WHERE i.id = ?`, id,
	).Scan(&inv.ID, &inv.OrderID, &inv.CustomerID, &inv.CustomerName, &inv.InvoiceNumber, &inv.Status, &inv.Amount, &inv.DueDate, &inv.PaidDate, &inv.CreatedAt)
	if err != nil {
		return nil, err
	}
	return inv, nil
}

func InvoiceMarkPaid(db *sql.DB, id int) error {
	inv, err := InvoiceGetByID(db, id)
	if err != nil {
		return err
	}

	// Update invoice status
	_, err = db.Exec("UPDATE invoices SET status = 'paid', paid_date = date('now') WHERE id = ?", id)
	if err != nil {
		return err
	}

	// Create GL journal entry: Debit Cash, Credit AR
	result, err := db.Exec(
		"INSERT INTO journal_entries (entry_date, description, reference) VALUES (date('now'), ?, ?)",
		fmt.Sprintf("Payment received for %s", inv.InvoiceNumber),
		inv.InvoiceNumber,
	)
	if err != nil {
		return err
	}
	jeID, _ := result.LastInsertId()

	// Debit Cash (1000)
	var cashID int
	db.QueryRow("SELECT id FROM gl_accounts WHERE code = '1000'").Scan(&cashID)
	db.Exec("INSERT INTO journal_lines (journal_entry_id, account_id, debit, credit) VALUES (?, ?, ?, 0)", jeID, cashID, inv.Amount)

	// Credit AR (1100)
	var arID int
	db.QueryRow("SELECT id FROM gl_accounts WHERE code = '1100'").Scan(&arID)
	db.Exec("INSERT INTO journal_lines (journal_entry_id, account_id, debit, credit) VALUES (?, ?, 0, ?)", jeID, arID, inv.Amount)

	// Update account balances
	db.Exec("UPDATE gl_accounts SET balance = balance + ? WHERE code = '1000'", inv.Amount) // Cash increases
	db.Exec("UPDATE gl_accounts SET balance = balance - ? WHERE code = '1100'", inv.Amount) // AR decreases

	return nil
}

func InvoiceCountByStatus(db *sql.DB, status string) int {
	var count int
	db.QueryRow("SELECT COUNT(*) FROM invoices WHERE status = ?", status).Scan(&count)
	return count
}

func InvoiceTotalOutstanding(db *sql.DB) float64 {
	var total float64
	db.QueryRow("SELECT COALESCE(SUM(amount), 0) FROM invoices WHERE status = 'pending'").Scan(&total)
	return total
}

func InvoiceGetByCustomerID(db *sql.DB, customerID int) ([]Invoice, error) {
	rows, err := db.Query(
		`SELECT i.id, i.order_id, i.customer_id, c.name, i.invoice_number, i.status, i.amount, i.due_date, i.paid_date, i.created_at
		FROM invoices i JOIN customers c ON i.customer_id = c.id WHERE i.customer_id = ? ORDER BY i.created_at DESC`, customerID,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var invoices []Invoice
	for rows.Next() {
		var inv Invoice
		if err := rows.Scan(&inv.ID, &inv.OrderID, &inv.CustomerID, &inv.CustomerName, &inv.InvoiceNumber, &inv.Status, &inv.Amount, &inv.DueDate, &inv.PaidDate, &inv.CreatedAt); err != nil {
			return nil, err
		}
		invoices = append(invoices, inv)
	}
	return invoices, nil
}
