package account

import (
	"context"
	"errors"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/TaruLab/keluargamart-backend/internal/db"
)

type Repository struct {
	pool *pgxpool.Pool
	q    *db.Queries
}

func NewRepository(pool *pgxpool.Pool) *Repository {
	return &Repository{pool: pool, q: db.New(pool)}
}

// List returns the buyer's address book, default shipping first.
func (r *Repository) List(ctx context.Context, userID int64) ([]db.BuyerAddress, error) {
	list, err := r.q.ListBuyerAddresses(ctx, userID)
	if err != nil {
		return nil, err
	}
	if len(list) == 0 {
		var name, phone, address, city string
		err := r.pool.QueryRow(ctx,
			`SELECT COALESCE(c.name, u.name), COALESCE(c.phone, ''), COALESCE(c.address, ''), COALESCE(c.city, 'Medan')
			 FROM users u
			 LEFT JOIN customers c ON c.user_id = u.id OR lower(c.email) = lower(u.email)
			 WHERE u.id = $1 AND c.address IS NOT NULL AND trim(c.address) != ''
			 LIMIT 1`,
			userID).Scan(&name, &phone, &address, &city)
		if err == nil && address != "" {
			created, createErr := r.Create(ctx, userID, AddressInput{
				Label:             "Alamat Utama",
				ContactName:       name,
				Phone:             phone,
				Line1:             address,
				City:              city,
				Country:           "Indonesia",
				IsDefaultShipping: true,
				IsDefaultBilling:  true,
			})
			if createErr == nil {
				return []db.BuyerAddress{created}, nil
			}
		}
	}
	return list, nil
}

// Get returns one address owned by userID.
func (r *Repository) Get(ctx context.Context, userID, id int64) (db.BuyerAddress, error) {
	a, err := r.q.GetBuyerAddress(ctx, db.GetBuyerAddressParams{ID: id, UserID: userID})
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return db.BuyerAddress{}, ErrNotFound
		}
		return db.BuyerAddress{}, err
	}
	return a, nil
}

// Create inserts the address. Default flags are honoured by clearing the
// previous defaults in the same transaction; the buyer's very first address
// becomes the default even when the payload omits the flags (mirrors the old
// localStorage behaviour the storefront migrated from).
func (r *Repository) Create(ctx context.Context, userID int64, in AddressInput) (db.BuyerAddress, error) {
	tx, err := r.pool.Begin(ctx)
	if err != nil {
		return db.BuyerAddress{}, err
	}
	defer func() { _ = tx.Rollback(ctx) }()
	q := r.q.WithTx(tx)

	isDefaultShipping, isDefaultBilling := in.IsDefaultShipping, in.IsDefaultBilling
	count, err := q.CountBuyerAddresses(ctx, userID)
	if err != nil {
		return db.BuyerAddress{}, err
	}
	if count == 0 {
		isDefaultShipping, isDefaultBilling = true, true
	}
	if isDefaultShipping {
		if err := q.ClearDefaultShipping(ctx, userID); err != nil {
			return db.BuyerAddress{}, err
		}
	}
	if isDefaultBilling {
		if err := q.ClearDefaultBilling(ctx, userID); err != nil {
			return db.BuyerAddress{}, err
		}
	}

	created, err := q.CreateBuyerAddress(ctx, db.CreateBuyerAddressParams{
		UserID:            userID,
		Label:             in.Label,
		ContactName:       in.ContactName,
		Phone:             in.Phone,
		Line1:             in.Line1,
		Line2:             pgText(in.Line2),
		City:              in.City,
		Province:          pgText(in.Province),
		PostalCode:        in.PostalCode,
		Country:           orDefault(in.Country, "Indonesia"),
		Latitude:          pgFloat(in.Latitude),
		Longitude:         pgFloat(in.Longitude),
		IsDefaultShipping: isDefaultShipping,
		IsDefaultBilling:  isDefaultBilling,
	})
	if err != nil {
		return db.BuyerAddress{}, err
	}
	if err := tx.Commit(ctx); err != nil {
		return db.BuyerAddress{}, err
	}
	return created, nil
}

// Update replaces the mutable fields of an address owned by userID. Setting a
// default flag clears the previous default in the same transaction.
func (r *Repository) Update(ctx context.Context, userID, id int64, in AddressInput) (db.BuyerAddress, error) {
	tx, err := r.pool.Begin(ctx)
	if err != nil {
		return db.BuyerAddress{}, err
	}
	defer func() { _ = tx.Rollback(ctx) }()
	q := r.q.WithTx(tx)

	if in.IsDefaultShipping {
		if err := q.ClearDefaultShipping(ctx, userID); err != nil {
			return db.BuyerAddress{}, err
		}
	}
	if in.IsDefaultBilling {
		if err := q.ClearDefaultBilling(ctx, userID); err != nil {
			return db.BuyerAddress{}, err
		}
	}

	updated, err := q.UpdateBuyerAddress(ctx, db.UpdateBuyerAddressParams{
		ID:                id,
		UserID:            userID,
		Label:             in.Label,
		ContactName:       in.ContactName,
		Phone:             in.Phone,
		Line1:             in.Line1,
		Line2:             pgText(in.Line2),
		City:              in.City,
		Province:          pgText(in.Province),
		PostalCode:        in.PostalCode,
		Country:           orDefault(in.Country, "Indonesia"),
		Latitude:          pgFloat(in.Latitude),
		Longitude:         pgFloat(in.Longitude),
		IsDefaultShipping: in.IsDefaultShipping,
		IsDefaultBilling:  in.IsDefaultBilling,
	})
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return db.BuyerAddress{}, ErrNotFound
		}
		return db.BuyerAddress{}, err
	}
	if err := tx.Commit(ctx); err != nil {
		return db.BuyerAddress{}, err
	}
	return updated, nil
}

// Delete removes an address owned by userID.
func (r *Repository) Delete(ctx context.Context, userID, id int64) error {
	rows, err := r.q.DeleteBuyerAddress(ctx, db.DeleteBuyerAddressParams{ID: id, UserID: userID})
	if err != nil {
		return err
	}
	if rows == 0 {
		return ErrNotFound
	}
	return nil
}
