package postgres

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/gofrs/uuid/v5"
	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"

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

// Repository implements the data access layer using sqlx + pgx.
type Repository struct {
	db *sqlx.DB
}

// New creates a Postgres-backed repository and configures pooling.
func New(db *sqlx.DB) *Repository {
	return &Repository{db: db}
}

func (r *Repository) CreateCompany(ctx context.Context, company *domain.Company) error {
	now := time.Now().UTC()
	company.CreatedAt = now
	company.UpdatedAt = now

	query := `INSERT INTO companies (id, cnpj, corporate_name, category, license_number, is_verified, latitude, longitude, city, state, phone, operating_hours, is_24_hours, created_at, updated_at)
VALUES (:id, :cnpj, :corporate_name, :category, :license_number, :is_verified, :latitude, :longitude, :city, :state, :phone, :operating_hours, :is_24_hours, :created_at, :updated_at)`

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

func (r *Repository) ListCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.Company, int64, error) {
	baseQuery := `FROM companies`
	var args []any
	var clauses []string

	if filter.Category != "" {
		clauses = append(clauses, fmt.Sprintf("category = $%d", len(args)+1))
		args = append(args, filter.Category)
	}
	if filter.Search != "" {
		clauses = append(clauses, fmt.Sprintf("(corporate_name ILIKE $%d OR cnpj ILIKE $%d)", len(args)+1, len(args)+1))
		args = append(args, "%"+filter.Search+"%")
	}
	if filter.City != "" {
		clauses = append(clauses, fmt.Sprintf("city = $%d", len(args)+1))
		args = append(args, filter.City)
	}
	if filter.State != "" {
		clauses = append(clauses, fmt.Sprintf("state = $%d", len(args)+1))
		args = append(args, filter.State)
	}
	if filter.IsVerified != nil {
		clauses = append(clauses, fmt.Sprintf("is_verified = $%d", len(args)+1))
		args = append(args, *filter.IsVerified)
	}

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	var total int64
	if err := r.db.GetContext(ctx, &total, "SELECT count(*) "+baseQuery+where, args...); err != nil {
		return nil, 0, err
	}

	if filter.Limit <= 0 {
		filter.Limit = 20
	}
	args = append(args, filter.Limit, filter.Offset)
	listQuery := fmt.Sprintf("SELECT id, cnpj, corporate_name, category, license_number, is_verified, latitude, longitude, city, state, phone, operating_hours, is_24_hours, created_at, updated_at %s%s ORDER BY created_at DESC LIMIT $%d OFFSET $%d", baseQuery, where, len(args)-1, len(args))

	var companies []domain.Company
	if err := r.db.SelectContext(ctx, &companies, listQuery, args...); err != nil {
		return nil, 0, err
	}
	return companies, total, nil
}

func (r *Repository) GetCompany(ctx context.Context, id uuid.UUID) (*domain.Company, error) {
	var company domain.Company
	query := `SELECT id, cnpj, corporate_name, category, license_number, is_verified, latitude, longitude, city, state, phone, operating_hours, is_24_hours, created_at, updated_at FROM companies WHERE id = $1`
	if err := r.db.GetContext(ctx, &company, query, id); err != nil {
		return nil, err
	}
	return &company, nil
}

func (r *Repository) UpdateCompany(ctx context.Context, company *domain.Company) error {
	company.UpdatedAt = time.Now().UTC()

	query := `UPDATE companies
SET cnpj = :cnpj, corporate_name = :corporate_name, category = :category, license_number = :license_number, is_verified = :is_verified, latitude = :latitude, longitude = :longitude, city = :city, state = :state, phone = :phone, operating_hours = :operating_hours, is_24_hours = :is_24_hours, updated_at = :updated_at
WHERE id = :id`

	res, err := r.db.NamedExecContext(ctx, query, company)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("company not found")
	}
	return nil
}

func (r *Repository) DeleteCompany(ctx context.Context, id uuid.UUID) error {
	var count int
	if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM users WHERE company_id = $1`, id); err != nil {
		return err
	}
	if count > 0 {
		return errors.New("company has related users")
	}
	if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM products WHERE seller_id = $1`, id); err != nil {
		return err
	}
	if count > 0 {
		return errors.New("company has related products")
	}
	if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM orders WHERE buyer_id = $1 OR seller_id = $1`, id); err != nil {
		return err
	}
	if count > 0 {
		return errors.New("company has related orders")
	}
	if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM reviews WHERE buyer_id = $1 OR seller_id = $1`, id); err != nil {
		return err
	}
	if count > 0 {
		return errors.New("company has related reviews")
	}
	if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM cart_items WHERE buyer_id = $1`, id); err != nil {
		return err
	}
	if count > 0 {
		return errors.New("company has related cart items")
	}

	res, err := r.db.ExecContext(ctx, `DELETE FROM companies WHERE id = $1`, id)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("company not found")
	}
	return nil
}

func (r *Repository) CreateProduct(ctx context.Context, product *domain.Product) error {
	// Removed batch, expires_at, stock
	query := `INSERT INTO products (id, seller_id, ean_code, name, description, manufacturer, category, subcategory, price_cents, observations, internal_code, factory_price_cents, pmc_cents, commercial_discount_cents, tax_substitution_cents, invoice_price_cents)
VALUES (:id, :seller_id, :ean_code, :name, :description, :manufacturer, :category, :subcategory, :price_cents, :observations, :internal_code, :factory_price_cents, :pmc_cents, :commercial_discount_cents, :tax_substitution_cents, :invoice_price_cents)
RETURNING created_at, updated_at`

	rows, err := r.db.NamedQueryContext(ctx, query, product)
	if err != nil {
		return err
	}
	defer rows.Close()

	if rows.Next() {
		if err := rows.Scan(&product.CreatedAt, &product.UpdatedAt); err != nil {
			return err
		}
	}
	return rows.Err()
}

func (r *Repository) BatchCreateProducts(ctx context.Context, products []domain.Product) error {
	if len(products) == 0 {
		return nil
	}

	tx, err := r.db.BeginTxx(ctx, nil)
	if err != nil {
		return err
	}

	query := `INSERT INTO products (id, seller_id, ean_code, name, description, manufacturer, category, subcategory, price_cents, observations, created_at, updated_at)
VALUES (:id, :seller_id, :ean_code, :name, :description, :manufacturer, :category, :subcategory, :price_cents, :observations, :created_at, :updated_at)`

	for _, p := range products {
		if _, err := tx.NamedExecContext(ctx, query, p); err != nil {
			_ = tx.Rollback()
			return err
		}
	}

	return tx.Commit()
}

func (r *Repository) ListProducts(ctx context.Context, filter domain.ProductFilter) ([]domain.Product, int64, error) {
	baseQuery := `FROM products`
	var args []any
	var clauses []string

	if filter.SellerID != nil {
		clauses = append(clauses, fmt.Sprintf("seller_id = $%d", len(args)+1))
		args = append(args, *filter.SellerID)
	}
	if filter.Search != "" {
		clauses = append(clauses, fmt.Sprintf("name ILIKE $%d", len(args)+1))
		args = append(args, "%"+filter.Search+"%")
	}

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	var total int64
	if err := r.db.GetContext(ctx, &total, "SELECT count(*) "+baseQuery+where, args...); err != nil {
		return nil, 0, err
	}

	if filter.Limit <= 0 {
		filter.Limit = 20
	}
	args = append(args, filter.Limit, filter.Offset)
	// REmoved batch, expires_at, stock columns from SELECT
	listQuery := fmt.Sprintf("SELECT id, seller_id, ean_code, name, description, manufacturer, category, subcategory, price_cents, observations, created_at, updated_at %s%s ORDER BY created_at DESC LIMIT $%d OFFSET $%d", baseQuery, where, len(args)-1, len(args))

	var products []domain.Product
	if err := r.db.SelectContext(ctx, &products, listQuery, args...); err != nil {
		return nil, 0, err
	}
	return products, total, nil
}

// ListRecords returns marketplace listings using a window function for total count.
func (r *Repository) ListRecords(ctx context.Context, filter domain.RecordSearchFilter) ([]domain.Product, int64, error) {
	baseQuery := `FROM products`
	var args []any
	var clauses []string

	if filter.Query != "" {
		clauses = append(clauses, fmt.Sprintf("(name ILIKE $%d OR description ILIKE $%d)", len(args)+1, len(args)+1))
		args = append(args, "%"+filter.Query+"%")
	}
	if filter.CreatedAfter != nil {
		clauses = append(clauses, fmt.Sprintf("created_at >= $%d", len(args)+1))
		args = append(args, *filter.CreatedAfter)
	}
	if filter.CreatedBefore != nil {
		clauses = append(clauses, fmt.Sprintf("created_at <= $%d", len(args)+1))
		args = append(args, *filter.CreatedBefore)
	}

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	sortColumns := map[string]string{
		"created_at": "created_at",
		"updated_at": "updated_at",
	}
	sortBy := sortColumns[strings.ToLower(filter.SortBy)]
	if sortBy == "" {
		sortBy = "updated_at"
	}
	sortOrder := strings.ToUpper(filter.SortOrder)
	if sortOrder != "ASC" {
		sortOrder = "DESC"
	}

	if filter.Limit <= 0 {
		filter.Limit = 20
	}
	args = append(args, filter.Limit, filter.Offset)

	// Removed batch, expires_at, stock from SELECT list
	listQuery := fmt.Sprintf(`SELECT id, seller_id, ean_code, name, description, manufacturer, category, subcategory, price_cents, observations, created_at, updated_at,
COUNT(*) OVER() AS total_count
%s%s ORDER BY %s %s LIMIT $%d OFFSET $%d`, baseQuery, where, sortBy, sortOrder, len(args)-1, len(args))

	type recordRow struct {
		domain.Product
		TotalCount int64 `db:"total_count"`
	}
	var rows []recordRow
	if err := r.db.SelectContext(ctx, &rows, listQuery, args...); err != nil {
		return nil, 0, err
	}

	total := int64(0)
	if len(rows) > 0 {
		total = rows[0].TotalCount
	}

	items := make([]domain.Product, 0, len(rows))
	for _, row := range rows {
		items = append(items, row.Product)
	}
	return items, total, nil
}

// SearchProducts returns products with distance from buyer, ordered by expiration date.
// Tenant info is anonymized (only city/state shown, not company name/ID).
func (r *Repository) SearchProducts(ctx context.Context, filter domain.ProductSearchFilter) ([]domain.ProductWithDistance, int64, error) {
	baseQuery := `FROM products p INNER JOIN companies c ON p.seller_id = c.id`
	var args []any
	var clauses []string

	if filter.Search != "" {
		// Use ILIKE for product name OR company name/city
		clauses = append(clauses, fmt.Sprintf("(p.name ILIKE $%d OR c.corporate_name ILIKE $%d OR c.city ILIKE $%d)", len(args)+1, len(args)+1, len(args)+1))
		args = append(args, "%"+filter.Search+"%")
	}
	if filter.MinPriceCents != nil {
		clauses = append(clauses, fmt.Sprintf("p.price_cents >= $%d", len(args)+1))
		args = append(args, *filter.MinPriceCents)
	}
	if filter.MaxPriceCents != nil {
		clauses = append(clauses, fmt.Sprintf("p.price_cents <= $%d", len(args)+1))
		args = append(args, *filter.MaxPriceCents)
	}
	if filter.ExpiresAfter != nil {
		clauses = append(clauses, fmt.Sprintf("p.expires_at >= $%d", len(args)+1))
		args = append(args, *filter.ExpiresAfter)
	}
	if filter.ExpiresBefore != nil {
		clauses = append(clauses, fmt.Sprintf("p.expires_at <= $%d", len(args)+1))
		args = append(args, *filter.ExpiresBefore)
	}
	if filter.ExcludeSellerID != nil {
		clauses = append(clauses, fmt.Sprintf("p.seller_id != $%d", len(args)+1))
		args = append(args, *filter.ExcludeSellerID)
	}

	// Always filter only available products - now using inventory_items stock_quantity
	clauses = append(clauses, "EXISTS (SELECT 1 FROM inventory_items i WHERE i.product_id = p.id AND i.stock_quantity > 0)")

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	var total int64
	countQuery := "SELECT count(*) " + baseQuery + where
	if err := r.db.GetContext(ctx, &total, countQuery, args...); err != nil {
		return nil, 0, err
	}

	if filter.Limit <= 0 {
		filter.Limit = 20
	}
	args = append(args, filter.Limit, filter.Offset)

	// Select products with tenant location info (anonymous: no company name/ID)
	// Subquery for total stock across inventory items
	listQuery := fmt.Sprintf(`
		SELECT p.id, p.seller_id, p.name, p.description, p.created_at, p.updated_at,
		       (SELECT COALESCE(SUM(stock_quantity), 0) FROM inventory_items WHERE product_id = p.id) as stock,
		       (SELECT sale_price_cents FROM inventory_items WHERE product_id = p.id ORDER BY updated_at DESC LIMIT 1) as price_cents,
		       (SELECT batch FROM inventory_items WHERE product_id = p.id ORDER BY updated_at DESC LIMIT 1) as batch,
		       (SELECT expires_at FROM inventory_items WHERE product_id = p.id ORDER BY updated_at DESC LIMIT 1) as expires_at,
		       c.city, c.state, c.latitude, c.longitude
		%s%s 
		ORDER BY p.name ASC 
		LIMIT $%d OFFSET $%d`, baseQuery, where, len(args)-1, len(args))

	type productRow struct {
		domain.Product
		City      string  `db:"city"`
		State     string  `db:"state"`
		Latitude  float64 `db:"latitude"`
		Longitude float64 `db:"longitude"`
	}

	var rows []productRow
	if err := r.db.SelectContext(ctx, &rows, listQuery, args...); err != nil {
		return nil, 0, err
	}

	// Calculate distance and build response
	results := make([]domain.ProductWithDistance, 0, len(rows))
	for _, row := range rows {
		var dist float64
		// If company has no location set (0,0), always include and show distance 0
		if row.Latitude == 0 && row.Longitude == 0 {
			dist = 0
		} else {
			dist = domain.HaversineDistance(filter.BuyerLat, filter.BuyerLng, row.Latitude, row.Longitude)
			// Filter by max distance only for companies with valid coordinates
			if filter.MaxDistanceKm != nil && dist > *filter.MaxDistanceKm {
				continue
			}
		}

		results = append(results, domain.ProductWithDistance{
			Product:     row.Product,
			DistanceKm:  dist,
			TenantCity:  row.City,
			TenantState: row.State,
		})
	}

	return results, total, nil
}

func (r *Repository) GetProduct(ctx context.Context, id uuid.UUID) (*domain.Product, error) {
	var product domain.Product
	query := `
		SELECT 
			id, seller_id, name, description, created_at, updated_at,
			(SELECT COALESCE(SUM(stock_quantity), 0) FROM inventory_items WHERE product_id = $1) as stock,
			(SELECT sale_price_cents FROM inventory_items WHERE product_id = $1 ORDER BY updated_at DESC LIMIT 1) as price_cents,
			(SELECT batch FROM inventory_items WHERE product_id = $1 ORDER BY updated_at DESC LIMIT 1) as batch,
			(SELECT expires_at FROM inventory_items WHERE product_id = $1 ORDER BY updated_at DESC LIMIT 1) as expires_at
		FROM products 
		WHERE id = $1`
	fmt.Printf("DEBUG: GetProduct Query ID: %s\n", id)
	if err := r.db.GetContext(ctx, &product, query, id); err != nil {
		fmt.Printf("DEBUG: GetProduct Error: %v\n", err)
		return nil, err
	}
	fmt.Printf("DEBUG: GetProduct Found: %+v\n", product)
	return &product, nil
}

func (r *Repository) UpdateProduct(ctx context.Context, product *domain.Product) error {
	query := `UPDATE products
SET seller_id = :seller_id, name = :name, description = :description, batch = :batch, expires_at = :expires_at, price_cents = :price_cents, stock = :stock
WHERE id = :id
RETURNING updated_at`

	rows, err := r.db.NamedQueryContext(ctx, query, product)
	if err != nil {
		return err
	}
	defer rows.Close()

	if !rows.Next() {
		if err := rows.Err(); err != nil {
			return err
		}
		return errors.New("product not found")
	}
	if err := rows.Scan(&product.UpdatedAt); err != nil {
		return err
	}
	return rows.Err()
}

func (r *Repository) DeleteProduct(ctx context.Context, id uuid.UUID) error {
	var count int
	if err := r.db.GetContext(ctx, &count, `SELECT COUNT(*) FROM order_items WHERE product_id = $1`, id); err != nil {
		return err
	}
	if count > 0 {
		return errors.New("product has related orders")
	}

	tx, err := r.db.BeginTxx(ctx, nil)
	if err != nil {
		return err
	}

	if _, err := tx.ExecContext(ctx, `DELETE FROM inventory_adjustments WHERE product_id = $1`, id); err != nil {
		_ = tx.Rollback()
		return err
	}
	if _, err := tx.ExecContext(ctx, `DELETE FROM cart_items WHERE product_id = $1`, id); err != nil {
		_ = tx.Rollback()
		return err
	}

	res, err := tx.ExecContext(ctx, `DELETE FROM products WHERE id = $1`, id)
	if err != nil {
		_ = tx.Rollback()
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		_ = tx.Rollback()
		return err
	}
	if rows == 0 {
		_ = tx.Rollback()
		return errors.New("product not found")
	}

	return tx.Commit()
}

func (r *Repository) CreateOrder(ctx context.Context, order *domain.Order) error {
	now := time.Now().UTC()
	order.CreatedAt = now
	order.UpdatedAt = now

	tx, err := r.db.BeginTxx(ctx, nil)
	if err != nil {
		return err
	}

	orderQuery := `INSERT INTO orders (id, buyer_id, seller_id, status, total_cents, payment_method, shipping_fee_cents, distance_km, shipping_recipient_name, shipping_street, shipping_number, shipping_complement, shipping_district, shipping_city, shipping_state, shipping_zip_code, shipping_country, shipping_latitude, shipping_longitude, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)`
	if _, err := tx.ExecContext(ctx, orderQuery, order.ID, order.BuyerID, order.SellerID, order.Status, order.TotalCents, order.PaymentMethod, order.ShippingFeeCents, order.DistanceKm, order.Shipping.RecipientName, order.Shipping.Street, order.Shipping.Number, order.Shipping.Complement, order.Shipping.District, order.Shipping.City, order.Shipping.State, order.Shipping.ZipCode, order.Shipping.Country, order.Shipping.Latitude, order.Shipping.Longitude, order.CreatedAt, order.UpdatedAt); err != nil {
		_ = tx.Rollback()
		return err
	}

	itemQuery := `INSERT INTO order_items (id, order_id, product_id, quantity, unit_cents, batch, expires_at) VALUES ($1, $2, $3, $4, $5, $6, $7)`
	for i := range order.Items {
		item := &order.Items[i]
		item.ID = uuid.Must(uuid.NewV7())
		item.OrderID = order.ID
		if _, err := tx.ExecContext(ctx, itemQuery, item.ID, item.OrderID, item.ProductID, item.Quantity, item.UnitCents, item.Batch, item.ExpiresAt); err != nil {
			_ = tx.Rollback()
			return err
		}

		// Reduce stock - updated to use inventory_items
		res, err := tx.ExecContext(ctx, `
			UPDATE inventory_items 
			SET stock_quantity = stock_quantity - $1, updated_at = $2 
			WHERE product_id = $3 
			AND stock_quantity >= $1 
			AND id = (
				SELECT id FROM inventory_items 
				WHERE product_id = $3 AND stock_quantity >= $1 
				ORDER BY expires_at ASC LIMIT 1
			)`, item.Quantity, now, item.ProductID)
		if err != nil {
			_ = tx.Rollback()
			return err
		}
		rows, err := res.RowsAffected()
		if err != nil {
			_ = tx.Rollback()
			return err
		}
		if rows == 0 {
			_ = tx.Rollback()
			return fmt.Errorf("insufficient stock for product %s", item.ProductID)
		}
	}

	return tx.Commit()
}

func (r *Repository) UpdateOrderItems(ctx context.Context, orderID uuid.UUID, items []domain.OrderItem, totalCents int64) error {
	tx, err := r.db.BeginTxx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	// 1. Update Order Total
	if _, err := tx.ExecContext(ctx, "UPDATE orders SET total_cents = $1, updated_at = $2 WHERE id = $3", totalCents, time.Now().UTC(), orderID); err != nil {
		return err
	}

	// 2. Clear existing items (be careful with stock, caller handles logic? Caller (Service) says it handles logic)
	// Assuming Service handles adjust inventory BEFORE calling this?
	// Service.UpdateOrderItems impl (step 643 line 93) just calls repo.
	// We just replace items here.
	if _, err := tx.ExecContext(ctx, "DELETE FROM order_items WHERE order_id = $1", orderID); err != nil {
		return err
	}

	// 3. Insert new items
	query := `INSERT INTO order_items (id, order_id, product_id, quantity, unit_cents, batch, expires_at)
	VALUES (:id, :order_id, :product_id, :quantity, :unit_cents, :batch, :expires_at)`

	for _, item := range items {
		if item.ID == uuid.Nil {
			item.ID = uuid.Must(uuid.NewV7())
		}
		item.OrderID = orderID
		if _, err := tx.NamedExecContext(ctx, query, item); err != nil {
			return err
		}
	}

	return tx.Commit()
}

func (r *Repository) ListOrders(ctx context.Context, filter domain.OrderFilter) ([]domain.Order, int64, error) {
	baseQuery := `FROM orders`
	var args []any
	var clauses []string

	if filter.BuyerID != nil {
		clauses = append(clauses, fmt.Sprintf("buyer_id = $%d", len(args)+1))
		args = append(args, *filter.BuyerID)
	}
	if filter.SellerID != nil {
		clauses = append(clauses, fmt.Sprintf("seller_id = $%d", len(args)+1))
		args = append(args, *filter.SellerID)
	}
	if filter.Status != "" {
		clauses = append(clauses, fmt.Sprintf("status = $%d", len(args)+1))
		args = append(args, filter.Status)
	}

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	var total int64
	if err := r.db.GetContext(ctx, &total, "SELECT count(*) "+baseQuery+where, args...); err != nil {
		return nil, 0, err
	}

	if filter.Limit <= 0 {
		filter.Limit = 20
	}
	args = append(args, filter.Limit, filter.Offset)
	listQuery := fmt.Sprintf(`SELECT id, buyer_id, seller_id, status, total_cents, payment_method, shipping_fee_cents, distance_km, COALESCE(shipping_recipient_name, '') as shipping_recipient_name, COALESCE(shipping_street, '') as shipping_street, COALESCE(shipping_number, '') as shipping_number, COALESCE(shipping_complement, '') as shipping_complement, COALESCE(shipping_district, '') as shipping_district, COALESCE(shipping_city, '') as shipping_city, COALESCE(shipping_state, '') as shipping_state, COALESCE(shipping_zip_code, '') as shipping_zip_code, COALESCE(shipping_country, '') as shipping_country, COALESCE(shipping_latitude, 0) as shipping_latitude, COALESCE(shipping_longitude, 0) as shipping_longitude, created_at, updated_at %s%s ORDER BY created_at DESC LIMIT $%d OFFSET $%d`, baseQuery, where, len(args)-1, len(args))

	var rows []struct {
		ID                    uuid.UUID            `db:"id"`
		BuyerID               uuid.UUID            `db:"buyer_id"`
		SellerID              uuid.UUID            `db:"seller_id"`
		Status                domain.OrderStatus   `db:"status"`
		TotalCents            int64                `db:"total_cents"`
		PaymentMethod         domain.PaymentMethod `db:"payment_method"`
		ShippingFeeCents      int64                `db:"shipping_fee_cents"`
		DistanceKm            float64              `db:"distance_km"`
		ShippingRecipientName string               `db:"shipping_recipient_name"`
		ShippingStreet        string               `db:"shipping_street"`
		ShippingNumber        string               `db:"shipping_number"`
		ShippingComplement    string               `db:"shipping_complement"`
		ShippingDistrict      string               `db:"shipping_district"`
		ShippingCity          string               `db:"shipping_city"`
		ShippingState         string               `db:"shipping_state"`
		ShippingZipCode       string               `db:"shipping_zip_code"`
		ShippingCountry       string               `db:"shipping_country"`
		ShippingLatitude      float64              `db:"shipping_latitude"`
		ShippingLongitude     float64              `db:"shipping_longitude"`
		CreatedAt             time.Time            `db:"created_at"`
		UpdatedAt             time.Time            `db:"updated_at"`
	}

	if err := r.db.SelectContext(ctx, &rows, listQuery, args...); err != nil {
		return nil, 0, err
	}

	orders := make([]domain.Order, 0, len(rows))
	itemQuery := `SELECT id, order_id, product_id, quantity, unit_cents, batch, expires_at FROM order_items WHERE order_id = $1`
	for _, row := range rows {
		var items []domain.OrderItem
		if err := r.db.SelectContext(ctx, &items, itemQuery, row.ID); err != nil {
			return nil, 0, err
		}
		orders = append(orders, domain.Order{
			ID:               row.ID,
			BuyerID:          row.BuyerID,
			SellerID:         row.SellerID,
			Status:           row.Status,
			TotalCents:       row.TotalCents,
			PaymentMethod:    row.PaymentMethod,
			ShippingFeeCents: row.ShippingFeeCents,
			DistanceKm:       row.DistanceKm,
			Items:            items,
			Shipping: domain.ShippingAddress{
				RecipientName: row.ShippingRecipientName,
				Street:        row.ShippingStreet,
				Number:        row.ShippingNumber,
				Complement:    row.ShippingComplement,
				District:      row.ShippingDistrict,
				City:          row.ShippingCity,
				State:         row.ShippingState,
				ZipCode:       row.ShippingZipCode,
				Country:       row.ShippingCountry,
				Latitude:      row.ShippingLatitude,
				Longitude:     row.ShippingLongitude,
			},
			CreatedAt: row.CreatedAt,
			UpdatedAt: row.UpdatedAt,
		})
	}
	return orders, total, nil
}

func (r *Repository) GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error) {
	var row struct {
		ID                    uuid.UUID            `db:"id"`
		BuyerID               uuid.UUID            `db:"buyer_id"`
		SellerID              uuid.UUID            `db:"seller_id"`
		Status                domain.OrderStatus   `db:"status"`
		TotalCents            int64                `db:"total_cents"`
		PaymentMethod         domain.PaymentMethod `db:"payment_method"`
		ShippingFeeCents      int64                `db:"shipping_fee_cents"`
		DistanceKm            float64              `db:"distance_km"`
		ShippingRecipientName string               `db:"shipping_recipient_name"`
		ShippingStreet        string               `db:"shipping_street"`
		ShippingNumber        string               `db:"shipping_number"`
		ShippingComplement    string               `db:"shipping_complement"`
		ShippingDistrict      string               `db:"shipping_district"`
		ShippingCity          string               `db:"shipping_city"`
		ShippingState         string               `db:"shipping_state"`
		ShippingZipCode       string               `db:"shipping_zip_code"`
		ShippingCountry       string               `db:"shipping_country"`
		ShippingLatitude      float64              `db:"shipping_latitude"`
		ShippingLongitude     float64              `db:"shipping_longitude"`
		CreatedAt             time.Time            `db:"created_at"`
		UpdatedAt             time.Time            `db:"updated_at"`
	}
	orderQuery := `SELECT id, buyer_id, seller_id, status, total_cents, payment_method, shipping_fee_cents, distance_km, COALESCE(shipping_recipient_name, '') as shipping_recipient_name, COALESCE(shipping_street, '') as shipping_street, COALESCE(shipping_number, '') as shipping_number, COALESCE(shipping_complement, '') as shipping_complement, COALESCE(shipping_district, '') as shipping_district, COALESCE(shipping_city, '') as shipping_city, COALESCE(shipping_state, '') as shipping_state, COALESCE(shipping_zip_code, '') as shipping_zip_code, COALESCE(shipping_country, '') as shipping_country, COALESCE(shipping_latitude, 0) as shipping_latitude, COALESCE(shipping_longitude, 0) as shipping_longitude, created_at, updated_at FROM orders WHERE id = $1`
	if err := r.db.GetContext(ctx, &row, orderQuery, id); err != nil {
		return nil, err
	}

	var items []domain.OrderItem
	itemQuery := `SELECT id, order_id, product_id, quantity, unit_cents, batch, expires_at FROM order_items WHERE order_id = $1`
	if err := r.db.SelectContext(ctx, &items, itemQuery, id); err != nil {
		return nil, err
	}
	order := &domain.Order{
		ID:               row.ID,
		BuyerID:          row.BuyerID,
		SellerID:         row.SellerID,
		Status:           row.Status,
		TotalCents:       row.TotalCents,
		PaymentMethod:    row.PaymentMethod,
		ShippingFeeCents: row.ShippingFeeCents,
		DistanceKm:       row.DistanceKm,
		Items:            items,
		Shipping: domain.ShippingAddress{
			RecipientName: row.ShippingRecipientName,
			Street:        row.ShippingStreet,
			Number:        row.ShippingNumber,
			Complement:    row.ShippingComplement,
			District:      row.ShippingDistrict,
			City:          row.ShippingCity,
			State:         row.ShippingState,
			ZipCode:       row.ShippingZipCode,
			Country:       row.ShippingCountry,
			Latitude:      row.ShippingLatitude,
			Longitude:     row.ShippingLongitude,
		},
		CreatedAt: row.CreatedAt,
		UpdatedAt: row.UpdatedAt,
	}
	return order, nil
}

func (r *Repository) UpdateOrderStatus(ctx context.Context, id uuid.UUID, status domain.OrderStatus) error {
	query := `UPDATE orders SET status = $1, updated_at = $2 WHERE id = $3`
	res, err := r.db.ExecContext(ctx, query, status, time.Now().UTC(), id)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("order not found")
	}
	return nil
}

func (r *Repository) DeleteOrder(ctx context.Context, id uuid.UUID) error {
	tx, err := r.db.BeginTxx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	// 1. Restore Stock for items being deleted
	// We must do this BEFORE deleting order_items
	var items []domain.OrderItem
	if err := tx.SelectContext(ctx, &items, `SELECT product_id, quantity FROM order_items WHERE order_id = $1`, id); err != nil {
		return err
	}

	for _, item := range items {
		// Increment stock back - updated to use inventory_items
		// We try to find the best matching inventory item by batch/expires_at if possible,
		// but since we don't store which specific inventory_item_id was used in order_items yet,
		// we update the one with the same batch and expires_at, or the latest one.
		query := `
			UPDATE inventory_items 
			SET stock_quantity = stock_quantity + $1, updated_at = $2 
			WHERE product_id = $3 
			AND (batch = $4 OR batch IS NULL)
			AND (expires_at = $5 OR expires_at IS NULL)
		`
		if _, err := tx.ExecContext(ctx, query, item.Quantity, time.Now().UTC(), item.ProductID, item.Batch, item.ExpiresAt); err != nil {
			return err
		}
	}

	// 2. Delete Dependencies
	if _, err := tx.ExecContext(ctx, `DELETE FROM reviews WHERE order_id = $1`, id); err != nil {
		return err
	}
	if _, err := tx.ExecContext(ctx, `DELETE FROM shipments WHERE order_id = $1`, id); err != nil {
		return err
	}
	if _, err := tx.ExecContext(ctx, `DELETE FROM order_items WHERE order_id = $1`, id); err != nil {
		return err
	}

	// 3. Delete Order
	res, err := tx.ExecContext(ctx, `DELETE FROM orders WHERE id = $1`, id)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("order not found")
	}

	return tx.Commit()
}

func (r *Repository) CreateShipment(ctx context.Context, shipment *domain.Shipment) error {
	now := time.Now().UTC()
	shipment.CreatedAt = now
	shipment.UpdatedAt = now

	query := `INSERT INTO shipments (id, order_id, carrier, tracking_code, external_tracking, status, created_at, updated_at)
VALUES (:id, :order_id, :carrier, :tracking_code, :external_tracking, :status, :created_at, :updated_at)`

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

func (r *Repository) GetShipmentByOrderID(ctx context.Context, orderID uuid.UUID) (*domain.Shipment, error) {
	var shipment domain.Shipment
	query := `SELECT id, order_id, carrier, tracking_code, external_tracking, status, created_at, updated_at FROM shipments WHERE order_id = $1`
	if err := r.db.GetContext(ctx, &shipment, query, orderID); err != nil {
		return nil, err
	}
	return &shipment, nil
}

func (r *Repository) AdjustInventory(ctx context.Context, productID uuid.UUID, delta int64, reason string) (*domain.InventoryItem, error) {
	// Adjust latest inventory_item for the given product_id
	query := `
		WITH target AS (
			SELECT id 
			FROM inventory_items 
			WHERE product_id = $2
			ORDER BY updated_at DESC 
			LIMIT 1
		)
		UPDATE inventory_items i
		SET stock_quantity = i.stock_quantity + $1, updated_at = $3
		FROM target
		WHERE i.id = target.id
		RETURNING 
			i.id, i.product_id, i.seller_id, i.sale_price_cents, i.stock_quantity, 
			i.batch, i.expires_at, i.observations, i.created_at, i.updated_at
	`

	var item domain.InventoryItem
	err := r.db.QueryRowxContext(ctx, query, delta, productID, time.Now().UTC()).Scan(
		&item.ID,
		&item.ProductID,
		&item.SellerID,
		&item.SalePriceCents,
		&item.StockQuantity,
		&item.Batch,
		&item.ExpiresAt,
		&item.Observations,
		&item.CreatedAt,
		&item.UpdatedAt,
	)
	if err != nil {
		return nil, err
	}

	return &item, nil
}

func (r *Repository) ListInventory(ctx context.Context, filter domain.InventoryFilter) ([]domain.InventoryItem, int64, error) {
	baseQuery := `FROM inventory_items i JOIN products p ON i.product_id = p.id`
	args := []any{}
	clauses := []string{}
	if filter.ExpiringBefore != nil {
		clauses = append(clauses, fmt.Sprintf("i.expires_at <= $%d", len(args)+1))
		args = append(args, *filter.ExpiringBefore)
	}
	if filter.SellerID != nil {
		clauses = append(clauses, fmt.Sprintf("i.seller_id = $%d", len(args)+1))
		args = append(args, *filter.SellerID)
	}

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	var total int64
	if err := r.db.GetContext(ctx, &total, "SELECT count(*) "+baseQuery+where, args...); err != nil {
		return nil, 0, err
	}

	if filter.Limit <= 0 {
		filter.Limit = 20
	}
	args = append(args, filter.Limit, filter.Offset)
	// Select columns matching InventoryItem struct db tags + product_name
	listQuery := fmt.Sprintf(`
		SELECT 
			i.id, i.product_id, i.seller_id, i.sale_price_cents, i.stock_quantity, 
			i.batch, i.expires_at, i.observations, i.created_at, i.updated_at,
			p.name AS product_name, p.ean_code 
		%s%s ORDER BY i.expires_at ASC LIMIT $%d OFFSET $%d`,
		baseQuery, where, len(args)-1, len(args))

	var items []domain.InventoryItem
	if err := r.db.SelectContext(ctx, &items, listQuery, args...); err != nil {
		return nil, 0, err
	}
	return items, total, nil
}

// GetInventoryItem fetches a single inventory item by ID.
func (r *Repository) GetInventoryItem(ctx context.Context, id uuid.UUID) (*domain.InventoryItem, error) {
	query := `
		SELECT 
			i.id, i.product_id, i.seller_id, i.sale_price_cents, i.stock_quantity, 
			i.batch, i.expires_at, i.observations, i.created_at, i.updated_at,
			p.name AS product_name, p.ean_code 
		FROM inventory_items i 
		JOIN products p ON i.product_id = p.id
		WHERE i.id = $1`

	var item domain.InventoryItem
	if err := r.db.GetContext(ctx, &item, query, id); err != nil {
		return nil, err
	}
	return &item, nil
}

// UpdateInventoryItem updates stock and price for an inventory record.
func (r *Repository) UpdateInventoryItem(ctx context.Context, item *domain.InventoryItem) error {
	query := `
		UPDATE inventory_items 
		SET stock_quantity = :stock_quantity, sale_price_cents = :sale_price_cents, updated_at = :updated_at
		WHERE id = :id`

	if _, err := r.db.NamedExecContext(ctx, query, item); err != nil {
		return err
	}
	return nil
}

func (r *Repository) CreateUser(ctx context.Context, user *domain.User) error {
	now := time.Now().UTC()
	user.CreatedAt = now
	user.UpdatedAt = now

	query := `INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, superadmin, nome_social, cpf, created_at, updated_at)
VALUES (:id, :company_id, :role, :name, :username, :email, :email_verified, :password_hash, :superadmin, :nome_social, :cpf, :created_at, :updated_at)`

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

func (r *Repository) ListUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) {
	baseQuery := `FROM users`
	var args []any
	var clauses []string

	if filter.CompanyID != nil {
		clauses = append(clauses, fmt.Sprintf("company_id = $%d", len(args)+1))
		args = append(args, *filter.CompanyID)
	}

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	countQuery := "SELECT count(*) " + baseQuery + where
	var total int64
	if err := r.db.GetContext(ctx, &total, countQuery, args...); err != nil {
		return nil, 0, err
	}

	args = append(args, filter.Limit, filter.Offset)
	listQuery := fmt.Sprintf("SELECT id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at %s%s ORDER BY created_at DESC LIMIT $%d OFFSET $%d", baseQuery, where, len(args)-1, len(args))

	var users []domain.User
	if err := r.db.SelectContext(ctx, &users, listQuery, args...); err != nil {
		return nil, 0, err
	}

	if len(users) > 0 {
		var userIDs []uuid.UUID
		var companyIDs []uuid.UUID
		companyMap := make(map[uuid.UUID]bool)

		for _, u := range users {
			userIDs = append(userIDs, u.ID)
			if u.CompanyID != uuid.Nil && !companyMap[u.CompanyID] {
				companyIDs = append(companyIDs, u.CompanyID)
				companyMap[u.CompanyID] = true
			}
		}

		// 1. Fetch Addresses
		var addresses []domain.Address
		queryAddr := `SELECT * FROM addresses WHERE entity_id = ANY($1)`
		if err := r.db.SelectContext(ctx, &addresses, queryAddr, pq.Array(userIDs)); err != nil {
			// Log error but don't fail the whole request? Or fail?
			// For now, let's fail to be safe, or just log.
			// Ideally we want the data.
			return nil, 0, fmt.Errorf("failed to fetch user addresses: %w", err)
		}

		// Map addresses to users
		addrMap := make(map[uuid.UUID][]domain.Address)
		for _, addr := range addresses {
			addrMap[addr.EntityID] = append(addrMap[addr.EntityID], addr)
		}

		// 2. Fetch Companies
		var companies []domain.Company
		if len(companyIDs) > 0 {
			queryComp := `SELECT * FROM companies WHERE id = ANY($1)`
			if err := r.db.SelectContext(ctx, &companies, queryComp, pq.Array(companyIDs)); err != nil {
				return nil, 0, fmt.Errorf("failed to fetch user companies: %w", err)
			}
		}

		// Map companies to users
		compMap := make(map[uuid.UUID]domain.Company)
		for _, c := range companies {
			compMap[c.ID] = c
		}

		// Populate Users
		for i := range users {
			// Add addresses
			if addrs, ok := addrMap[users[i].ID]; ok {
				users[i].Enderecos = addrs
			} else {
				users[i].Enderecos = []domain.Address{} // Ensure empty array not nil
			}

			// Add company data
			if comp, ok := compMap[users[i].CompanyID]; ok {
				users[i].EmpresasDados = []domain.Company{comp}
			} else {
				users[i].EmpresasDados = []domain.Company{}
			}
		}
	}

	return users, total, nil
}

func (r *Repository) GetUser(ctx context.Context, id uuid.UUID) (*domain.User, error) {
	var user domain.User
	query := `SELECT id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at FROM users WHERE id = $1`
	if err := r.db.GetContext(ctx, &user, query, id); err != nil {
		return nil, err
	}
	return &user, nil
}

func (r *Repository) GetUserByUsername(ctx context.Context, username string) (*domain.User, error) {
	var user domain.User
	query := `SELECT id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at FROM users WHERE username = $1`
	if err := r.db.GetContext(ctx, &user, query, username); err != nil {
		return nil, err
	}
	return &user, nil
}

func (r *Repository) GetUserByEmail(ctx context.Context, email string) (*domain.User, error) {
	var user domain.User
	query := `SELECT id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at FROM users WHERE email = $1`
	if err := r.db.GetContext(ctx, &user, query, email); err != nil {
		return nil, err
	}
	return &user, nil
}

func (r *Repository) UpdateUser(ctx context.Context, user *domain.User) error {
	user.UpdatedAt = time.Now().UTC()

	query := `UPDATE users
SET company_id = :company_id, role = :role, name = :name, username = :username, email = :email, email_verified = :email_verified, password_hash = :password_hash, updated_at = :updated_at
WHERE id = :id`

	res, err := r.db.NamedExecContext(ctx, query, user)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("user not found")
	}
	return nil
}

func (r *Repository) DeleteUser(ctx context.Context, id uuid.UUID) error {
	res, err := r.db.ExecContext(ctx, "DELETE FROM users WHERE id = $1", id)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("user not found")
	}
	return nil
}

func (r *Repository) AddCartItem(ctx context.Context, item *domain.CartItem) (*domain.CartItem, error) {
	now := time.Now().UTC()
	item.CreatedAt = now
	item.UpdatedAt = now

	query := `INSERT INTO cart_items (id, buyer_id, product_id, quantity, unit_cents, batch, expires_at, created_at, updated_at)
VALUES (:id, :buyer_id, :product_id, :quantity, :unit_cents, :batch, :expires_at, :created_at, :updated_at)
ON CONFLICT (buyer_id, product_id) DO UPDATE
SET quantity = cart_items.quantity + EXCLUDED.quantity,
    unit_cents = EXCLUDED.unit_cents,
    batch = EXCLUDED.batch,
    expires_at = EXCLUDED.expires_at,
    updated_at = EXCLUDED.updated_at
RETURNING id, buyer_id, product_id, quantity, unit_cents, batch, expires_at, created_at, updated_at`

	var saved domain.CartItem
	rows, err := r.db.NamedQueryContext(ctx, query, item)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	if rows.Next() {
		if err := rows.StructScan(&saved); err != nil {
			return nil, err
		}
		return &saved, nil
	}
	return nil, errors.New("failed to persist cart item")
}

// ReplaceCart clears the buyer's cart and inserts new items transactionally.
func (r *Repository) ReplaceCart(ctx context.Context, buyerID uuid.UUID, items []domain.CartItem) error {
	tx, err := r.db.BeginTxx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	// Clear existing cart
	if _, err := tx.ExecContext(ctx, "DELETE FROM cart_items WHERE buyer_id = $1", buyerID); err != nil {
		return err
	}

	// Insert new items
	query := `INSERT INTO cart_items (id, buyer_id, product_id, quantity, unit_cents, batch, expires_at, created_at, updated_at)
              VALUES (:id, :buyer_id, :product_id, :quantity, :unit_cents, :batch, :expires_at, :created_at, :updated_at)`

	now := time.Now().UTC()
	for _, item := range items {
		if item.ID == uuid.Nil {
			item.ID = uuid.Must(uuid.NewV7())
		}
		item.CreatedAt = now
		item.UpdatedAt = now
		if _, err := tx.NamedExecContext(ctx, query, item); err != nil {
			return err
		}
	}

	return tx.Commit()
}

func (r *Repository) ListCartItems(ctx context.Context, buyerID uuid.UUID) ([]domain.CartItem, error) {
	query := `SELECT ci.id, ci.buyer_id, ci.product_id, ci.quantity, ci.unit_cents, ci.batch, ci.expires_at, ci.created_at, ci.updated_at,
       p.name AS product_name
FROM cart_items ci
JOIN products p ON p.id = ci.product_id
WHERE ci.buyer_id = $1
ORDER BY ci.created_at DESC`

	var items []domain.CartItem
	if err := r.db.SelectContext(ctx, &items, query, buyerID); err != nil {
		return nil, err
	}
	return items, nil
}

func (r *Repository) DeleteCartItem(ctx context.Context, id uuid.UUID, buyerID uuid.UUID) error {
	res, err := r.db.ExecContext(ctx, "DELETE FROM cart_items WHERE id = $1 AND buyer_id = $2", id, buyerID)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("cart item not found")
	}
	return nil
}

func (r *Repository) DeleteCartItemByProduct(ctx context.Context, buyerID, productID uuid.UUID) error {
	result, err := r.db.ExecContext(ctx, "DELETE FROM cart_items WHERE buyer_id = $1 AND product_id = $2", buyerID, productID)
	if err != nil {
		return err
	}
	rows, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("cart item not found")
	}
	return nil
}

func (r *Repository) ClearCart(ctx context.Context, buyerID uuid.UUID) error {
	_, err := r.db.ExecContext(ctx, "DELETE FROM cart_items WHERE buyer_id = $1", buyerID)
	return err
}

func (r *Repository) CreateReview(ctx context.Context, review *domain.Review) error {
	now := time.Now().UTC()
	review.CreatedAt = now

	query := `INSERT INTO reviews (id, order_id, buyer_id, seller_id, rating, comment, created_at)
VALUES (:id, :order_id, :buyer_id, :seller_id, :rating, :comment, :created_at)`

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

func (r *Repository) GetCompanyRating(ctx context.Context, companyID uuid.UUID) (*domain.CompanyRating, error) {
	var row struct {
		Avg   *float64 `db:"avg"`
		Count int64    `db:"count"`
	}
	query := `SELECT AVG(rating)::float AS avg, COUNT(*) AS count FROM reviews WHERE seller_id = $1`
	if err := r.db.GetContext(ctx, &row, query, companyID); err != nil {
		return nil, err
	}

	avg := 0.0
	if row.Avg != nil {
		avg = *row.Avg
	}

	return &domain.CompanyRating{CompanyID: companyID, AverageScore: avg, TotalReviews: row.Count}, nil
}

func (r *Repository) SellerDashboard(ctx context.Context, sellerID uuid.UUID) (*domain.SellerDashboard, error) {
	var sales struct {
		Total  *int64 `db:"total"`
		Orders int64  `db:"orders"`
	}

	baseStatuses := []string{string(domain.OrderStatusPaid), string(domain.OrderStatusInvoiced), string(domain.OrderStatusDelivered)}
	if err := r.db.GetContext(ctx, &sales, `SELECT COALESCE(SUM(total_cents), 0)::BIGINT AS total, COUNT(*) AS orders FROM orders WHERE seller_id = $1 AND status = ANY($2::text[])`, sellerID, pq.Array(baseStatuses)); err != nil {
		return nil, err
	}

	var topProducts []domain.TopProduct
	topQuery := `SELECT oi.product_id, p.name, CAST(SUM(oi.quantity) AS BIGINT) AS total_quantity, CAST(SUM(oi.quantity * oi.unit_cents) AS BIGINT) AS revenue_cents
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
JOIN products p ON p.id = oi.product_id
WHERE o.seller_id = $1 AND o.status = ANY($2::text[])
GROUP BY oi.product_id, p.name
ORDER BY total_quantity DESC
LIMIT 5`
	if err := r.db.SelectContext(ctx, &topProducts, topQuery, sellerID, pq.Array(baseStatuses)); err != nil {
		return nil, err
	}

	var lowStock []domain.Product
	lowStockQuery := `
		SELECT 
			p.id, p.seller_id, p.ean_code, p.name, p.description, p.manufacturer, 
			p.category, p.subcategory, p.price_cents, p.observations, 
			p.created_at, p.updated_at,
			CAST(COALESCE(SUM(i.stock_quantity), 0) AS BIGINT) as stock,
			'' as batch,
			'0001-01-01'::DATE as expires_at,
			'' as internal_code,
			0::BIGINT as factory_price_cents,
			0::BIGINT as pmc_cents,
			0::BIGINT as commercial_discount_cents,
			0::BIGINT as tax_substitution_cents,
			0::BIGINT as invoice_price_cents
		FROM products p
		LEFT JOIN inventory_items i ON p.id = i.product_id
		WHERE p.seller_id = $1
		GROUP BY p.id
		HAVING COALESCE(SUM(i.stock_quantity), 0) <= 10
		ORDER BY stock ASC, p.updated_at DESC
		LIMIT 10`

	if err := r.db.SelectContext(ctx, &lowStock, lowStockQuery, sellerID); err != nil {
		return nil, err
	}

	total := int64(0)
	if sales.Total != nil {
		total = *sales.Total
	}

	return &domain.SellerDashboard{
		SellerID:        sellerID,
		TotalSalesCents: total,
		OrdersCount:     sales.Orders,
		TopProducts:     topProducts,
		LowStockAlerts:  lowStock,
	}, nil
}

func (r *Repository) AdminDashboard(ctx context.Context, since time.Time) (*domain.AdminDashboard, error) {
	var gmv *int64
	if err := r.db.GetContext(ctx, &gmv, `SELECT COALESCE(SUM(total_cents), 0)::BIGINT FROM orders WHERE status = ANY($1::text[])`, pq.Array([]string{string(domain.OrderStatusPaid), string(domain.OrderStatusInvoiced), string(domain.OrderStatusDelivered)})); err != nil {
		return nil, err
	}

	var newCompanies int64
	if err := r.db.GetContext(ctx, &newCompanies, `SELECT COUNT(*) FROM companies WHERE created_at >= $1`, since); err != nil {
		return nil, err
	}

	totalGMV := int64(0)
	if gmv != nil {
		totalGMV = *gmv
	}

	return &domain.AdminDashboard{GMVCents: totalGMV, NewCompanies: newCompanies, WindowStartAt: since}, nil
}

func (r *Repository) GetShippingMethodsByVendor(ctx context.Context, vendorID uuid.UUID) ([]domain.ShippingMethod, error) {
	query := `SELECT id, vendor_id, type, active, preparation_minutes, max_radius_km, min_fee_cents, price_per_km_cents, free_shipping_threshold_cents, pickup_address, pickup_hours, created_at, updated_at
FROM shipping_methods WHERE vendor_id = $1 ORDER BY type`
	var methods []domain.ShippingMethod
	if err := r.db.SelectContext(ctx, &methods, query, vendorID); err != nil {
		return nil, err
	}
	return methods, nil
}

func (r *Repository) UpsertShippingMethods(ctx context.Context, methods []domain.ShippingMethod) error {
	if len(methods) == 0 {
		return nil
	}
	query := `INSERT INTO shipping_methods (id, vendor_id, type, active, preparation_minutes, max_radius_km, min_fee_cents, price_per_km_cents, free_shipping_threshold_cents, pickup_address, pickup_hours, created_at, updated_at)
VALUES (:id, :vendor_id, :type, :active, :preparation_minutes, :max_radius_km, :min_fee_cents, :price_per_km_cents, :free_shipping_threshold_cents, :pickup_address, :pickup_hours, :created_at, :updated_at)
ON CONFLICT (vendor_id, type) DO UPDATE
SET active = EXCLUDED.active,
    preparation_minutes = EXCLUDED.preparation_minutes,
    max_radius_km = EXCLUDED.max_radius_km,
    min_fee_cents = EXCLUDED.min_fee_cents,
    price_per_km_cents = EXCLUDED.price_per_km_cents,
    free_shipping_threshold_cents = EXCLUDED.free_shipping_threshold_cents,
    pickup_address = EXCLUDED.pickup_address,
    pickup_hours = EXCLUDED.pickup_hours,
    updated_at = EXCLUDED.updated_at`

	tx, err := r.db.BeginTxx(ctx, nil)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			_ = tx.Rollback()
		}
	}()

	now := time.Now().UTC()
	for i := range methods {
		methods[i].CreatedAt = now
		methods[i].UpdatedAt = now
		if _, err = tx.NamedExecContext(ctx, query, methods[i]); err != nil {
			return err
		}
	}

	if err = tx.Commit(); err != nil {
		return err
	}
	return nil
}

func (r *Repository) ListReviews(ctx context.Context, filter domain.ReviewFilter) ([]domain.Review, int64, error) {
	baseQuery := `FROM reviews`
	var args []any
	var clauses []string

	if filter.SellerID != nil {
		clauses = append(clauses, fmt.Sprintf("seller_id = $%d", len(args)+1))
		args = append(args, *filter.SellerID)
	}

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	var total int64
	if err := r.db.GetContext(ctx, &total, "SELECT count(*) "+baseQuery+where, args...); err != nil {
		return nil, 0, err
	}

	if filter.Limit <= 0 {
		filter.Limit = 20
	}
	args = append(args, filter.Limit, filter.Offset)

	listQuery := fmt.Sprintf("SELECT id, order_id, buyer_id, seller_id, rating, comment, created_at %s%s ORDER BY created_at DESC LIMIT $%d OFFSET $%d", baseQuery, where, len(args)-1, len(args))

	var reviews []domain.Review
	if err := r.db.SelectContext(ctx, &reviews, listQuery, args...); err != nil {
		return nil, 0, err
	}
	return reviews, total, nil
}

func (r *Repository) ListShipments(ctx context.Context, filter domain.ShipmentFilter) ([]domain.Shipment, int64, error) {
	baseQuery := `FROM shipments s`
	var args []any
	var clauses []string

	if filter.SellerID != nil {
		baseQuery += ` JOIN orders o ON s.order_id = o.id`
		clauses = append(clauses, fmt.Sprintf("o.seller_id = $%d", len(args)+1))
		args = append(args, *filter.SellerID)
	}

	where := ""
	if len(clauses) > 0 {
		where = " WHERE " + strings.Join(clauses, " AND ")
	}

	var total int64
	if err := r.db.GetContext(ctx, &total, "SELECT count(*) "+baseQuery+where, args...); err != nil {
		return nil, 0, err
	}

	if filter.Limit <= 0 {
		filter.Limit = 20
	}
	args = append(args, filter.Limit, filter.Offset)

	listQuery := fmt.Sprintf("SELECT s.id, s.order_id, s.carrier, s.tracking_code, s.external_tracking, s.status, s.created_at, s.updated_at %s%s ORDER BY s.created_at DESC LIMIT $%d OFFSET $%d", baseQuery, where, len(args)-1, len(args))

	var shipments []domain.Shipment
	if err := r.db.SelectContext(ctx, &shipments, listQuery, args...); err != nil {
		return nil, 0, err
	}
	return shipments, total, nil
}

func (r *Repository) GetShippingSettings(ctx context.Context, vendorID uuid.UUID) (*domain.ShippingSettings, error) {
	var settings domain.ShippingSettings
	err := r.db.GetContext(ctx, &settings, `SELECT * FROM shipping_settings WHERE vendor_id = $1`, vendorID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) || err.Error() == "sql: no rows in result set" {
			return nil, nil
		}
		return nil, err
	}
	return &settings, nil
}

func (r *Repository) UpsertShippingSettings(ctx context.Context, settings *domain.ShippingSettings) error {
	now := time.Now().UTC()
	settings.UpdatedAt = now

	// Create if new
	if settings.CreatedAt.IsZero() {
		settings.CreatedAt = now
	}

	query := `
		INSERT INTO shipping_settings (
			vendor_id, active, max_radius_km, price_per_km_cents, min_fee_cents, 
			free_shipping_threshold_cents, pickup_active, pickup_address, pickup_hours, 
			latitude, longitude, created_at, updated_at
		) VALUES (
			:vendor_id, :active, :max_radius_km, :price_per_km_cents, :min_fee_cents,
			:free_shipping_threshold_cents, :pickup_active, :pickup_address, :pickup_hours,
			:latitude, :longitude, :created_at, :updated_at
		) 
		ON CONFLICT (vendor_id) DO UPDATE SET
			active = :active,
			max_radius_km = :max_radius_km,
			price_per_km_cents = :price_per_km_cents,
			min_fee_cents = :min_fee_cents,
			free_shipping_threshold_cents = :free_shipping_threshold_cents,
			pickup_active = :pickup_active,
			pickup_address = :pickup_address,
			pickup_hours = :pickup_hours,
			latitude = :latitude,
			longitude = :longitude,
			updated_at = :updated_at
	`

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

// GetPaymentGatewayConfig retrieves admin config for a provider (e.g. stripe).
func (r *Repository) GetPaymentGatewayConfig(ctx context.Context, provider string) (*domain.PaymentGatewayConfig, error) {
	var cfg domain.PaymentGatewayConfig
	query := `SELECT provider, active, credentials, environment, commission, updated_at FROM payment_gateway_configs WHERE provider = $1`
	if err := r.db.GetContext(ctx, &cfg, query, provider); err != nil {
		return nil, err
	}
	return &cfg, nil
}

// UpsertPaymentGatewayConfig updates or creates global gateway settings.
func (r *Repository) UpsertPaymentGatewayConfig(ctx context.Context, config *domain.PaymentGatewayConfig) error {
	config.UpdatedAt = time.Now().UTC()
	query := `INSERT INTO payment_gateway_configs (provider, active, credentials, environment, commission, updated_at)
VALUES (:provider, :active, :credentials, :environment, :commission, :updated_at)
ON CONFLICT (provider) DO UPDATE SET
active = EXCLUDED.active, credentials = EXCLUDED.credentials, environment = EXCLUDED.environment, commission = EXCLUDED.commission, updated_at = EXCLUDED.updated_at`
	_, err := r.db.NamedExecContext(ctx, query, config)
	return err
}

// GetSellerPaymentAccount retrieves seller's connect account.
func (r *Repository) GetSellerPaymentAccount(ctx context.Context, sellerID uuid.UUID) (*domain.SellerPaymentAccount, error) {
	var acc domain.SellerPaymentAccount
	query := `SELECT seller_id, gateway, account_id, account_type, status, created_at FROM seller_payment_accounts WHERE seller_id = $1`
	if err := r.db.GetContext(ctx, &acc, query, sellerID); err != nil {
		return nil, err
	}
	return &acc, nil
}

// UpsertSellerPaymentAccount updates or creates seller's connect account.
func (r *Repository) UpsertSellerPaymentAccount(ctx context.Context, account *domain.SellerPaymentAccount) error {
	query := `INSERT INTO seller_payment_accounts (seller_id, gateway, account_id, account_type, status, created_at)
VALUES (:seller_id, :gateway, :account_id, :account_type, :status, :created_at)
ON CONFLICT (seller_id, gateway) DO UPDATE SET
account_id = EXCLUDED.account_id, account_type = EXCLUDED.account_type, status = EXCLUDED.status`
	_, err := r.db.NamedExecContext(ctx, query, account)
	return err
}

func (r *Repository) CreateAddress(ctx context.Context, address *domain.Address) error {
	query := `INSERT INTO addresses (id, entity_id, title, zip_code, street, number, complement, district, city, state, created_at, updated_at)
	VALUES (:id, :entity_id, :title, :zip_code, :street, :number, :complement, :district, :city, :state, :created_at, :updated_at)`

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

func (r *Repository) ListAddresses(ctx context.Context, entityID uuid.UUID) ([]domain.Address, error) {
	var addresses []domain.Address
	query := `SELECT id, entity_id, title, zip_code, street, number, complement, district, city, state, latitude, longitude, created_at, updated_at FROM addresses WHERE entity_id = $1 ORDER BY created_at DESC`
	err := r.db.SelectContext(ctx, &addresses, query, entityID)
	// If no rows, SelectContext returns nil error but empty slice if initialized, or maybe error?
	// sqlx returns error if slice is empty? No, SelectContext handles empty result by returning empty slice usually.
	return addresses, err
}

func (r *Repository) GetAddress(ctx context.Context, id uuid.UUID) (*domain.Address, error) {
	var addr domain.Address
	err := r.db.GetContext(ctx, &addr, `SELECT id, entity_id, title, zip_code, street, number, complement, district, city, state, latitude, longitude, created_at, updated_at FROM addresses WHERE id = $1`, id)
	if err != nil {
		return nil, err
	}
	return &addr, nil
}

func (r *Repository) UpdateAddress(ctx context.Context, address *domain.Address) error {
	address.UpdatedAt = time.Now().UTC()
	query := `UPDATE addresses SET title=:title, zip_code=:zip_code, street=:street, number=:number, complement=:complement, district=:district, city=:city, state=:state, updated_at=:updated_at WHERE id=:id`
	_, err := r.db.NamedExecContext(ctx, query, address)
	return err
}

func (r *Repository) DeleteAddress(ctx context.Context, id uuid.UUID) error {
	_, err := r.db.ExecContext(ctx, `DELETE FROM addresses WHERE id = $1`, id)
	return err
}

func (r *Repository) ListManufacturers(ctx context.Context) ([]string, error) {
	query := `SELECT DISTINCT manufacturer FROM products WHERE manufacturer IS NOT NULL AND manufacturer != '' ORDER BY manufacturer ASC`
	var manufacturers []string
	if err := r.db.SelectContext(ctx, &manufacturers, query); err != nil {
		return nil, err
	}
	return manufacturers, nil
}

func (r *Repository) ListCategories(ctx context.Context) ([]string, error) {
	query := `SELECT DISTINCT category FROM products WHERE category IS NOT NULL AND category != '' ORDER BY category ASC`
	var categories []string
	if err := r.db.SelectContext(ctx, &categories, query); err != nil {
		return nil, err
	}
	return categories, nil
}

func (r *Repository) GetProductByEAN(ctx context.Context, ean string) (*domain.Product, error) {
	var product domain.Product
	query := `SELECT id, seller_id, ean_code, name, description, manufacturer, category, subcategory, price_cents, internal_code, factory_price_cents, pmc_cents, commercial_discount_cents, tax_substitution_cents, invoice_price_cents, observations, created_at, updated_at FROM products WHERE ean_code = $1 LIMIT 1`
	if err := r.db.GetContext(ctx, &product, query, ean); err != nil {
		return nil, err
	}
	return &product, nil
}

func (r *Repository) CreateInventoryItem(ctx context.Context, item *domain.InventoryItem) error {
	query := `INSERT INTO inventory_items (id, product_id, seller_id, sale_price_cents, stock_quantity, batch, expires_at, observations, created_at, updated_at) VALUES (:id, :product_id, :seller_id, :sale_price_cents, :stock_quantity, :batch, :expires_at, :observations, :created_at, :updated_at)`
	if item.ID == uuid.Nil {
		item.ID = uuid.Must(uuid.NewV7())
	}
	item.CreatedAt = time.Now().UTC()
	item.UpdatedAt = time.Now().UTC()

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