package repository

import (
	"context"
	"fmt"

	dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
	"github.com/jackc/pgx/v5/pgtype"
)

func convertDBTransaction(transaction dbgen.Transaction) domain.Transaction {
	return domain.Transaction{
		ID:               transaction.ID,
		Amount:           domain.Currency(transaction.Amount),
		BranchID:         transaction.BranchID,
		CashierID:        transaction.CashierID.Int64,
		BetID:            transaction.BetID.Int64,
		NumberOfOutcomes: transaction.NumberOfOutcomes.Int64,
		Type:             domain.TransactionType(transaction.Type.Int64),
		PaymentOption:    domain.PaymentOption(transaction.PaymentOption.Int64),
		FullName:         transaction.FullName.String,
		PhoneNumber:      transaction.PhoneNumber.String,
		BankCode:         transaction.BankCode.String,
		BeneficiaryName:  transaction.BeneficiaryName.String,
		AccountName:      transaction.AccountName.String,
		AccountNumber:    transaction.AccountNumber.String,
		ReferenceNumber:  transaction.ReferenceNumber.String,
		ApprovedBy: domain.ValidInt64{
			Value: transaction.ApprovedBy.Int64,
			Valid: transaction.ApprovedBy.Valid,
		},
		CreatedAt:      transaction.CreatedAt.Time,
		UpdatedAt:      transaction.UpdatedAt.Time,
		Verified:       transaction.Verified,
		BranchName:     transaction.BranchName.String,
		BranchLocation: transaction.BranchLocation.String,
		CashierName:    transaction.CashierName.String,
		CompanyID:      transaction.CompanyID.Int64,
		ApproverName: domain.ValidString{
			Value: transaction.ApproverName.String,
			Valid: transaction.ApprovedBy.Valid,
		},
	}
}

func convertCreateTransaction(transaction domain.CreateTransaction) dbgen.CreateTransactionParams {
	return dbgen.CreateTransactionParams{
		Amount:           int64(transaction.Amount),
		BranchID:         transaction.BranchID,
		CashierID:        pgtype.Int8{Int64: transaction.CashierID, Valid: true},
		BetID:            pgtype.Int8{Int64: transaction.BetID, Valid: true},
		Type:             pgtype.Int8{Int64: int64(transaction.Type), Valid: true},
		PaymentOption:    pgtype.Int8{Int64: int64(transaction.PaymentOption), Valid: true},
		FullName:         pgtype.Text{String: transaction.FullName, Valid: transaction.FullName != ""},
		PhoneNumber:      pgtype.Text{String: transaction.PhoneNumber, Valid: transaction.PhoneNumber != ""},
		BankCode:         pgtype.Text{String: transaction.BankCode, Valid: transaction.BankCode != ""},
		BeneficiaryName:  pgtype.Text{String: transaction.BeneficiaryName, Valid: transaction.BeneficiaryName != ""},
		AccountName:      pgtype.Text{String: transaction.AccountName, Valid: transaction.AccountName != ""},
		AccountNumber:    pgtype.Text{String: transaction.AccountNumber, Valid: transaction.AccountNumber != ""},
		ReferenceNumber:  pgtype.Text{String: transaction.ReferenceNumber, Valid: transaction.ReferenceNumber != ""},
		NumberOfOutcomes: pgtype.Int8{Int64: transaction.NumberOfOutcomes, Valid: true},
		BranchName:       pgtype.Text{String: transaction.BranchName, Valid: transaction.BranchName != ""},
		BranchLocation:   pgtype.Text{String: transaction.BranchLocation, Valid: transaction.BranchLocation != ""},
		CashierName:      pgtype.Text{String: transaction.CashierName, Valid: transaction.CashierName != ""},
		CompanyID:        pgtype.Int8{Int64: transaction.CompanyID, Valid: true},
	}
}

func (s *Store) CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error) {

	newTransaction, err := s.queries.CreateTransaction(ctx, convertCreateTransaction(transaction))
	if err != nil {
		return domain.Transaction{}, err
	}
	return convertDBTransaction(newTransaction), err
}

func (s *Store) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) {
	transaction, err := s.queries.GetTransactionByID(ctx, id)
	if err != nil {
		return domain.Transaction{}, err
	}

	return convertDBTransaction(transaction), nil
}

func (s *Store) GetAllTransactions(ctx context.Context, filter domain.TransactionFilter) ([]domain.Transaction, error) {
	transaction, err := s.queries.GetAllTransactions(ctx, dbgen.GetAllTransactionsParams{
		BranchID: pgtype.Int8{
			Int64: filter.BranchID.Value,
			Valid: filter.BranchID.Valid,
		},
		CompanyID: pgtype.Int8{
			Int64: filter.CompanyID.Value,
			Valid: filter.CompanyID.Valid,
		},
		CashierID: pgtype.Int8{
			Int64: filter.CashierID.Value,
			Valid: filter.CashierID.Valid,
		},
	})

	if err != nil {
		return nil, err
	}

	var result []domain.Transaction = make([]domain.Transaction, 0, len(transaction))
	for _, ticket := range transaction {
		result = append(result, convertDBTransaction(ticket))
	}
	return result, nil
}
func (s *Store) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) {
	transaction, err := s.queries.GetTransactionByBranch(ctx, id)

	if err != nil {
		return nil, err
	}

	var result []domain.Transaction = make([]domain.Transaction, 0, len(transaction))
	for _, ticket := range transaction {
		result = append(result, convertDBTransaction(ticket))
	}
	return result, nil
}

func (s *Store) UpdateTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error {
	err := s.queries.UpdateTransactionVerified(ctx, dbgen.UpdateTransactionVerifiedParams{
		ID: id,
		ApprovedBy: pgtype.Int8{
			Int64: approvedBy,
			Valid: true,
		},
		Verified: verified,
		ApproverName: pgtype.Text{
			String: approverName,
			Valid:  true,
		},
	})
	return err
}

// GetTransactionTotals returns total deposits and withdrawals
func (s *Store) GetTransactionTotals(ctx context.Context, filter domain.ReportFilter) (deposits, withdrawals domain.Currency, err error) {
	query := `SELECT
		COALESCE(SUM(CASE WHEN type = 1 THEN amount ELSE 0 END), 0) as deposits,
		COALESCE(SUM(CASE WHEN type = 0 THEN amount ELSE 0 END), 0) as withdrawals
		FROM transactions`

	args := []interface{}{}
	argPos := 1

	if filter.CompanyID.Valid {
		query += fmt.Sprintf(" WHERE company_id = $%d", argPos)
		args = append(args, filter.CompanyID.Value)
		argPos++
	} else if filter.BranchID.Valid {
		query += fmt.Sprintf(" WHERE branch_id = $%d", argPos)
		args = append(args, filter.BranchID.Value)
		argPos++
	} else if filter.UserID.Valid {
		query += fmt.Sprintf(" WHERE cashier_id = $%d", argPos)
		args = append(args, filter.UserID.Value)
		argPos++
	}

	if filter.StartTime.Valid {
		query += fmt.Sprintf(" AND %screated_at >= $%d", func() string {
			if len(args) == 0 {
				return ""
			}
			return " "
		}(), argPos)
		args = append(args, filter.StartTime.Value)
		argPos++
	}
	if filter.EndTime.Valid {
		query += fmt.Sprintf(" AND created_at <= $%d", argPos)
		args = append(args, filter.EndTime.Value)
		argPos++
	}

	row := s.conn.QueryRow(ctx, query, args...)
	err = row.Scan(&deposits, &withdrawals)
	if err != nil {
		return 0, 0, fmt.Errorf("failed to get transaction totals: %w", err)
	}
	return deposits, withdrawals, nil
}

// GetBranchTransactionTotals returns transaction totals by branch
func (s *Store) GetBranchTransactionTotals(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.BranchTransactions, error) {
	query := `SELECT 
		branch_id,
		COALESCE(SUM(CASE WHEN type = 1 THEN amount ELSE 0 END), 0) as deposits,
		COALESCE(SUM(CASE WHEN type = 0 THEN amount ELSE 0 END), 0) as withdrawals
		FROM transactions`

	args := []interface{}{}
	argPos := 1

	if filter.CompanyID.Valid {
		query += fmt.Sprintf(" WHERE company_id = $%d", argPos)
		args = append(args, filter.CompanyID.Value)
		argPos++
	}
	if filter.StartTime.Valid {
		query += fmt.Sprintf(" AND %screated_at >= $%d", func() string {
			if len(args) == 0 {
				return " WHERE "
			}
			return " AND "
		}(), argPos)
		args = append(args, filter.StartTime.Value)
		argPos++
	}
	if filter.EndTime.Valid {
		query += fmt.Sprintf(" AND created_at <= $%d", argPos)
		args = append(args, filter.EndTime.Value)
		argPos++
	}

	query += " GROUP BY branch_id"

	rows, err := s.conn.Query(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("failed to query branch transaction totals: %w", err)
	}
	defer rows.Close()

	totals := make(map[int64]domain.BranchTransactions)
	for rows.Next() {
		var branchID int64
		var transactions domain.BranchTransactions
		if err := rows.Scan(&branchID, &transactions.Deposits, &transactions.Withdrawals); err != nil {
			return nil, fmt.Errorf("failed to scan branch transaction totals: %w", err)
		}
		totals[branchID] = transactions
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("rows error: %w", err)
	}
	return totals, nil
}
