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 convertCreateBranch(branch domain.CreateBranch) dbgen.CreateBranchParams {
	return dbgen.CreateBranchParams{
		Name:            branch.Name,
		Location:        branch.Location,
		WalletID:        branch.WalletID,
		BranchManagerID: branch.BranchManagerID,
		CompanyID:       branch.CompanyID,
		IsSelfOwned:     branch.IsSelfOwned,
	}
}

func convertDBBranchDetail(dbBranch dbgen.BranchDetail) domain.BranchDetail {
	return domain.BranchDetail{
		ID:                 dbBranch.ID,
		Name:               dbBranch.Name,
		Location:           dbBranch.Location,
		WalletID:           dbBranch.WalletID,
		BranchManagerID:    dbBranch.BranchManagerID,
		CompanyID:          dbBranch.CompanyID,
		IsSelfOwned:        dbBranch.IsSelfOwned,
		ManagerName:        dbBranch.ManagerName.(string),
		ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String,
		Balance:            domain.Currency(dbBranch.Balance.Int64),
	}
}

func convertDBBranch(dbBranch dbgen.Branch) domain.Branch {
	return domain.Branch{
		ID:              dbBranch.ID,
		Name:            dbBranch.Name,
		Location:        dbBranch.Location,
		WalletID:        dbBranch.WalletID,
		BranchManagerID: dbBranch.BranchManagerID,
		CompanyID:       dbBranch.CompanyID,
		IsSelfOwned:     dbBranch.IsSelfOwned,
	}
}

func convertUpdateBranch(updateBranch domain.UpdateBranch) dbgen.UpdateBranchParams {

	var newUpdateBranch dbgen.UpdateBranchParams

	newUpdateBranch.ID = updateBranch.ID

	if updateBranch.Name != nil {
		newUpdateBranch.Name = pgtype.Text{
			String: *updateBranch.Name,
			Valid:  true,
		}
	}
	if updateBranch.Location != nil {
		newUpdateBranch.Location = pgtype.Text{
			String: *updateBranch.Location,
			Valid:  true,
		}
	}
	if updateBranch.BranchManagerID != nil {
		newUpdateBranch.BranchManagerID = pgtype.Int8{
			Int64: *updateBranch.BranchManagerID,
			Valid: true,
		}
	}
	if updateBranch.CompanyID != nil {
		newUpdateBranch.CompanyID = pgtype.Int8{
			Int64: *updateBranch.CompanyID,
			Valid: true,
		}
	}
	if updateBranch.IsSelfOwned != nil {
		newUpdateBranch.IsSelfOwned = pgtype.Bool{
			Bool:  *updateBranch.IsSelfOwned,
			Valid: true,
		}
	}
	if updateBranch.IsActive != nil {
		newUpdateBranch.IsActive = pgtype.Bool{
			Bool:  *updateBranch.IsActive,
			Valid: true,
		}
	}

	return newUpdateBranch
}

func (s *Store) CreateBranch(ctx context.Context, branch domain.CreateBranch) (domain.Branch, error) {

	dbBranch, err := s.queries.CreateBranch(ctx, convertCreateBranch(branch))

	if err != nil {
		return domain.Branch{}, err
	}
	return convertDBBranch(dbBranch), nil
}

func (s *Store) GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error) {
	dbBranch, err := s.queries.GetBranchByID(ctx, id)
	if err != nil {
		return domain.BranchDetail{}, err
	}
	return convertDBBranchDetail(dbBranch), nil
}

func (s *Store) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error) {
	dbBranches, err := s.queries.GetBranchByManagerID(ctx, branchManagerID)
	if err != nil {
		return nil, err
	}
	var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches))
	for _, dbBranch := range dbBranches {
		branches = append(branches, convertDBBranchDetail(dbBranch))
	}
	return branches, nil
}
func (s *Store) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error) {
	dbBranches, err := s.queries.GetBranchByCompanyID(ctx, companyID)
	if err != nil {
		return nil, err
	}
	var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches))
	for _, dbBranch := range dbBranches {
		branches = append(branches, convertDBBranchDetail(dbBranch))
	}
	return branches, nil
}

func (s *Store) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error) {
	dbBranches, err := s.queries.GetAllBranches(ctx)
	if err != nil {
		return nil, err
	}
	var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches))
	for _, dbBranch := range dbBranches {
		branches = append(branches, convertDBBranchDetail(dbBranch))
	}
	return branches, nil
}

func (s *Store) SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error) {
	dbBranches, err := s.queries.SearchBranchByName(ctx, pgtype.Text{String: name, Valid: true})
	if err != nil {
		return nil, err
	}

	var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches))
	for _, dbBranch := range dbBranches {
		branches = append(branches, convertDBBranchDetail(dbBranch))
	}
	return branches, nil
}

func (s *Store) UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error) {

	dbBranch, err := s.queries.UpdateBranch(ctx, convertUpdateBranch(branch))
	if err != nil {
		return domain.Branch{}, err
	}
	return convertDBBranch(dbBranch), nil
}

func (s *Store) DeleteBranch(ctx context.Context, id int64) error {
	return s.queries.DeleteBranch(ctx, id)
}

// Branch Operations

func (s *Store) CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error {
	_, err := s.queries.CreateBranchOperation(ctx, dbgen.CreateBranchOperationParams{
		BranchID:    branchOperation.BranchID,
		OperationID: branchOperation.OperationID,
	})
	return err
}

func (s *Store) CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error) {
	dbSupportedOperation, err := s.queries.CreateSupportedOperation(ctx, dbgen.CreateSupportedOperationParams{
		Name:        supportedOperation.Name,
		Description: supportedOperation.Description,
	})
	if err != nil {
		return domain.SupportedOperation{}, err
	}
	return domain.SupportedOperation{
		ID:          dbSupportedOperation.ID,
		Name:        dbSupportedOperation.Name,
		Description: dbSupportedOperation.Description,
	}, nil
}

func (s *Store) CreateBranchCashier(ctx context.Context, branchID int64, userID int64) error {
	_, err := s.queries.CreateBranchCashier(ctx, dbgen.CreateBranchCashierParams{
		UserID:   userID,
		BranchID: branchID,
	})

	if err != nil {
		return err
	}
	return nil
}

func (s *Store) GetAllSupportedOperations(ctx context.Context) ([]domain.SupportedOperation, error) {
	dbOperations, err := s.queries.GetAllSupportedOperations(ctx)
	if err != nil {
		return nil, err
	}

	var operations []domain.SupportedOperation = make([]domain.SupportedOperation, 0, len(dbOperations))
	for _, dbOperation := range dbOperations {
		operations = append(operations, domain.SupportedOperation{
			ID:          dbOperation.ID,
			Name:        dbOperation.Name,
			Description: dbOperation.Description,
		})
	}
	return operations, nil

}

func (s *Store) GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) {
	dbBranchOperations, err := s.queries.GetBranchOperations(ctx, branchID)
	if err != nil {
		return nil, err
	}
	var branchOperations []domain.BranchOperation = make([]domain.BranchOperation, 0, len(dbBranchOperations))
	for _, dbBranchOperation := range dbBranchOperations {
		branchOperations = append(branchOperations, domain.BranchOperation{
			ID:                   dbBranchOperation.ID,
			OperationName:        dbBranchOperation.Name,
			OperationDescription: dbBranchOperation.Description,
		})
	}
	return branchOperations, nil
}

func (s *Store) GetBranchByCashier(ctx context.Context, userID int64) (domain.Branch, error) {
	branch, err := s.queries.GetBranchByCashier(ctx, userID)
	if err != nil {
		return domain.Branch{}, err
	}

	return convertDBBranch(branch), err
}

func (s *Store) DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error {
	err := s.queries.DeleteBranchOperation(ctx, dbgen.DeleteBranchOperationParams{
		BranchID:    branchID,
		OperationID: operationID,
	})
	return err
}

func (s *Store) DeleteBranchCashier(ctx context.Context, userID int64) error {
	return s.queries.DeleteBranchCashier(ctx, userID)

}

// GetBranchCounts returns total and active branch counts
func (s *Store) GetBranchCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) {
	query := `SELECT 
        COUNT(*) as total,
        COUNT(CASE WHEN is_active = true THEN 1 END) as active,
        COUNT(CASE WHEN is_active = false THEN 1 END) as inactive
        FROM branches`

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

	// Add filters if provided
	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++
	}

	row := s.conn.QueryRow(ctx, query, args...)
	err = row.Scan(&total, &active, &inactive)
	if err != nil {
		return 0, 0, 0, fmt.Errorf("failed to get branch counts: %w", err)
	}

	return total, active, inactive, nil
}

// GetBranchDetails returns branch details map
func (s *Store) GetBranchDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.BranchDetail, error) {
	query := `SELECT 
        b.id, 
        b.name, 
        b.location,
        CONCAT(u.first_name, ' ', u.last_name) as manager_name
        FROM branches b
        LEFT JOIN users u ON b.branch_manager_id = u.id`

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

	// Add filters if provided
	if filter.CompanyID.Valid {
		query += fmt.Sprintf(" WHERE b.company_id = $%d", argPos)
		args = append(args, filter.CompanyID.Value)
		argPos++
	}
	if filter.BranchID.Valid {
		query += fmt.Sprintf(" AND %sb.id = $%d", func() string {
			if len(args) == 0 {
				return " WHERE "
			}
			return " AND "
		}(), argPos)
		args = append(args, filter.BranchID.Value)
		argPos++
	}
	if filter.StartTime.Valid {
		query += fmt.Sprintf(" AND %sb.created_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 b.created_at <= $%d", argPos)
		args = append(args, filter.EndTime.Value)
		argPos++
	}

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

	details := make(map[int64]domain.BranchDetail)
	for rows.Next() {
		var id int64
		var detail domain.BranchDetail
		if err := rows.Scan(&id, &detail.Name, &detail.Location, &detail.ManagerName); err != nil {
			return nil, fmt.Errorf("failed to scan branch detail: %w", err)
		}
		details[id] = detail
	}

	if err = rows.Err(); err != nil {
		return nil, fmt.Errorf("rows error: %w", err)
	}

	return details, nil
}

// In internal/repository/branch.go
func (s *Store) GetAllCompaniesBranch(ctx context.Context) ([]domain.Company, error) {
	dbCompanies, err := s.queries.GetAllCompanies(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get all companies: %w", err)
	}

	companies := make([]domain.Company, 0, len(dbCompanies))
	for _, dbCompany := range dbCompanies {
		companies = append(companies, domain.Company{
			ID:       dbCompany.ID,
			Name:     dbCompany.Name,
			WalletID: dbCompany.WalletID,
			AdminID:  dbCompany.AdminID,
		})
	}

	return companies, nil
}

// In internal/repository/branch.go
func (s *Store) GetBranchesByCompany(ctx context.Context, companyID int64) ([]domain.Branch, error) {
	dbBranches, err := s.queries.GetBranchByCompanyID(ctx, companyID)
	if err != nil {
		return nil, fmt.Errorf("failed to get branches for company %d: %w", companyID, err)
	}

	branches := make([]domain.Branch, 0, len(dbBranches))
	for _, dbBranch := range dbBranches {
		branch := domain.Branch{
			ID:          dbBranch.ID,
			Name:        dbBranch.Name,
			Location:    dbBranch.Location,
			WalletID:    dbBranch.WalletID,
			CompanyID:   dbBranch.CompanyID,
			IsSelfOwned: dbBranch.IsSelfOwned,
		}

		branch.BranchManagerID = dbBranch.BranchManagerID

		branches = append(branches, branch)
	}

	return branches, nil
}
