package postgres

import (
	"context"
	"time"

	"github.com/gofrs/uuid/v5"
	"github.com/saveinmed/backend-go/internal/domain"
)

// CreateDocument persists a KYC document.
func (r *Repository) CreateDocument(ctx context.Context, doc *domain.CompanyDocument) error {
	now := time.Now().UTC()
	doc.CreatedAt = now
	doc.UpdatedAt = now

	query := `INSERT INTO company_documents (id, company_id, type, url, status, rejection_reason, created_at, updated_at)
VALUES (:id, :company_id, :type, :url, :status, :rejection_reason, :created_at, :updated_at)`

	_, err := r.db.NamedExecContext(ctx, query, doc)
	return err
}

// ListDocuments retrieves documents for a company.
func (r *Repository) ListDocuments(ctx context.Context, companyID uuid.UUID) ([]domain.CompanyDocument, error) {
	var docs []domain.CompanyDocument
	query := `SELECT id, company_id, type, url, status, rejection_reason, created_at, updated_at FROM company_documents WHERE company_id = $1 ORDER BY created_at DESC`
	if err := r.db.SelectContext(ctx, &docs, query, companyID); err != nil {
		return nil, err
	}
	return docs, nil
}

// RecordLedgerEntry inserts an immutable financial record.
func (r *Repository) RecordLedgerEntry(ctx context.Context, entry *domain.LedgerEntry) error {
	entry.CreatedAt = time.Now().UTC()
	query := `INSERT INTO ledger_entries (id, company_id, amount_cents, type, description, reference_id, created_at)
VALUES (:id, :company_id, :amount_cents, :type, :description, :reference_id, :created_at)`

	_, err := r.db.NamedExecContext(ctx, query, entry)
	return err
}

// GetLedger returns transaction history for a company.
func (r *Repository) GetLedger(ctx context.Context, companyID uuid.UUID, limit, offset int) ([]domain.LedgerEntry, int64, error) {
	var entries []domain.LedgerEntry

	// Get total count
	var total int64
	if err := r.db.GetContext(ctx, &total, "SELECT count(*) FROM ledger_entries WHERE company_id = $1", companyID); err != nil {
		return nil, 0, err
	}

	// Get paginated entries
	query := `SELECT id, company_id, amount_cents, type, description, reference_id, created_at FROM ledger_entries WHERE company_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3`
	if err := r.db.SelectContext(ctx, &entries, query, companyID, limit, offset); err != nil {
		return nil, 0, err
	}
	return entries, total, nil
}

// GetBalance calculates the current balance based on ledger entries.
func (r *Repository) GetBalance(ctx context.Context, companyID uuid.UUID) (int64, error) {
	var balance int64
	// COALESCE to handle case with no entries returning NULL
	query := `SELECT COALESCE(SUM(amount_cents), 0) FROM ledger_entries WHERE company_id = $1`
	if err := r.db.GetContext(ctx, &balance, query, companyID); err != nil {
		return 0, err
	}
	return balance, nil
}

// CreateWithdrawal requests a payout.
func (r *Repository) CreateWithdrawal(ctx context.Context, withdrawal *domain.Withdrawal) error {
	now := time.Now().UTC()
	withdrawal.CreatedAt = now
	withdrawal.UpdatedAt = now

	// Transaction to ensure balance check?
	// In a real system, we reserved balance via ledger entry first.
	// The Service layer should call RecordLedgerEntry(WITHDRAWAL) before calling CreateWithdrawal.
	// So here we just insert the request.

	query := `INSERT INTO withdrawals (id, company_id, amount_cents, status, bank_account_info, created_at, updated_at)
VALUES (:id, :company_id, :amount_cents, :status, :bank_account_info, :created_at, :updated_at)`

	_, err := r.db.NamedExecContext(ctx, query, withdrawal)
	return err
}

// ListWithdrawals retrieves payout requests.
func (r *Repository) ListWithdrawals(ctx context.Context, companyID uuid.UUID) ([]domain.Withdrawal, error) {
	var list []domain.Withdrawal
	query := `SELECT id, company_id, amount_cents, status, bank_account_info, transaction_id, rejection_reason, created_at, updated_at FROM withdrawals WHERE company_id = $1 ORDER BY created_at DESC`
	if err := r.db.SelectContext(ctx, &list, query, companyID); err != nil {
		return nil, err
	}
	return list, nil
}
