package repository

import (
	"apskel-pos-be/internal/entities"
	"context"
	"database/sql"

	"github.com/google/uuid"
)

type ProductIngredientRepository struct {
	db *sql.DB
}

func NewProductIngredientRepository(db *sql.DB) *ProductIngredientRepository {
	return &ProductIngredientRepository{db: db}
}

func (r *ProductIngredientRepository) Create(ctx context.Context, productIngredient *entities.ProductIngredient) error {
	query := `
		INSERT INTO product_ingredients (id, organization_id, outlet_id, product_id, ingredient_id, quantity, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
	`

	_, err := r.db.ExecContext(ctx, query,
		productIngredient.ID,
		productIngredient.OrganizationID,
		productIngredient.OutletID,
		productIngredient.ProductID,
		productIngredient.IngredientID,
		productIngredient.Quantity,
		productIngredient.CreatedAt,
		productIngredient.UpdatedAt,
	)

	return err
}

func (r *ProductIngredientRepository) GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.ProductIngredient, error) {
	query := `
		SELECT pi.id, pi.organization_id, pi.outlet_id, pi.product_id, pi.ingredient_id, pi.quantity, pi.created_at, pi.updated_at,
		       p.id, p.organization_id, p.category_id, p.sku, p.name, p.description, p.price, p.cost, p.business_type, p.image_url, p.printer_type, p.unit_id, p.has_ingredients, p.metadata, p.is_active, p.created_at, p.updated_at,
		       i.id, i.organization_id, i.outlet_id, i.name, i.unit_id, i.cost, i.stock, i.is_semi_finished, i.is_active, i.metadata, i.created_at, i.updated_at
		FROM product_ingredients pi
		LEFT JOIN products p ON pi.product_id = p.id
		LEFT JOIN ingredients i ON pi.ingredient_id = i.id
		WHERE pi.id = $1 AND pi.organization_id = $2
	`

	productIngredient := &entities.ProductIngredient{}
	product := &entities.Product{}
	ingredient := &entities.Ingredient{}

	err := r.db.QueryRowContext(ctx, query, id, organizationID).Scan(
		&productIngredient.ID,
		&productIngredient.OrganizationID,
		&productIngredient.OutletID,
		&productIngredient.ProductID,
		&productIngredient.IngredientID,
		&productIngredient.Quantity,
		&productIngredient.CreatedAt,
		&productIngredient.UpdatedAt,
		&product.ID,
		&product.OrganizationID,
		&product.CategoryID,
		&product.SKU,
		&product.Name,
		&product.Description,
		&product.Price,
		&product.Cost,
		&product.BusinessType,
		&product.ImageURL,
		&product.PrinterType,
		&product.UnitID,
		&product.HasIngredients,
		&product.Metadata,
		&product.IsActive,
		&product.CreatedAt,
		&product.UpdatedAt,
		&ingredient.ID,
		&ingredient.OrganizationID,
		&ingredient.OutletID,
		&ingredient.Name,
		&ingredient.UnitID,
		&ingredient.Cost,
		&ingredient.Stock,
		&ingredient.IsSemiFinished,
		&ingredient.IsActive,
		&ingredient.Metadata,
		&ingredient.CreatedAt,
		&ingredient.UpdatedAt,
	)

	if err != nil {
		return nil, err
	}

	productIngredient.Product = product
	productIngredient.Ingredient = ingredient
	return productIngredient, nil
}

func (r *ProductIngredientRepository) GetByProductID(ctx context.Context, productID, organizationID uuid.UUID) ([]*entities.ProductIngredient, error) {
	query := `
		SELECT pi.id, pi.organization_id, pi.outlet_id, pi.product_id, pi.ingredient_id, pi.quantity, pi.created_at, pi.updated_at,
		       p.id, p.organization_id, p.category_id, p.sku, p.name, p.description, p.price, p.cost, p.business_type, p.image_url, p.printer_type, p.unit_id, p.has_ingredients, p.metadata, p.is_active, p.created_at, p.updated_at,
		       i.id, i.organization_id, i.outlet_id, i.name, i.unit_id, i.cost, i.stock, i.is_semi_finished, i.is_active, i.metadata, i.created_at, i.updated_at
		FROM product_ingredients pi
		LEFT JOIN products p ON pi.product_id = p.id
		LEFT JOIN ingredients i ON pi.ingredient_id = i.id
		WHERE pi.product_id = $1 AND pi.organization_id = $2
		ORDER BY pi.created_at DESC
	`

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

	var productIngredients []*entities.ProductIngredient
	for rows.Next() {
		productIngredient := &entities.ProductIngredient{}
		product := &entities.Product{}
		ingredient := &entities.Ingredient{}

		err := rows.Scan(
			&productIngredient.ID,
			&productIngredient.OrganizationID,
			&productIngredient.OutletID,
			&productIngredient.ProductID,
			&productIngredient.IngredientID,
			&productIngredient.Quantity,
			&productIngredient.CreatedAt,
			&productIngredient.UpdatedAt,
			&product.ID,
			&product.OrganizationID,
			&product.CategoryID,
			&product.SKU,
			&product.Name,
			&product.Description,
			&product.Price,
			&product.Cost,
			&product.BusinessType,
			&product.ImageURL,
			&product.PrinterType,
			&product.UnitID,
			&product.HasIngredients,
			&product.Metadata,
			&product.IsActive,
			&product.CreatedAt,
			&product.UpdatedAt,
			&ingredient.ID,
			&ingredient.OrganizationID,
			&ingredient.OutletID,
			&ingredient.Name,
			&ingredient.UnitID,
			&ingredient.Cost,
			&ingredient.Stock,
			&ingredient.IsSemiFinished,
			&ingredient.IsActive,
			&ingredient.Metadata,
			&ingredient.CreatedAt,
			&ingredient.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}

		productIngredient.Product = product
		productIngredient.Ingredient = ingredient
		productIngredients = append(productIngredients, productIngredient)
	}

	return productIngredients, nil
}

func (r *ProductIngredientRepository) GetByIngredientID(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.ProductIngredient, error) {
	query := `
		SELECT pi.id, pi.organization_id, pi.outlet_id, pi.product_id, pi.ingredient_id, pi.quantity, pi.created_at, pi.updated_at,
		       p.id, p.organization_id, p.category_id, p.sku, p.name, p.description, p.price, p.cost, p.business_type, p.image_url, p.printer_type, p.unit_id, p.has_ingredients, p.metadata, p.is_active, p.created_at, p.updated_at,
		       i.id, i.organization_id, i.outlet_id, i.name, i.unit_id, i.cost, i.stock, i.is_semi_finished, i.is_active, i.metadata, i.created_at, i.updated_at
		FROM product_ingredients pi
		LEFT JOIN products p ON pi.product_id = p.id
		LEFT JOIN ingredients i ON pi.ingredient_id = i.id
		WHERE pi.ingredient_id = $1 AND pi.organization_id = $2
		ORDER BY pi.created_at DESC
	`

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

	var productIngredients []*entities.ProductIngredient
	for rows.Next() {
		productIngredient := &entities.ProductIngredient{}
		product := &entities.Product{}
		ingredient := &entities.Ingredient{}

		err := rows.Scan(
			&productIngredient.ID,
			&productIngredient.OrganizationID,
			&productIngredient.OutletID,
			&productIngredient.ProductID,
			&productIngredient.IngredientID,
			&productIngredient.Quantity,
			&productIngredient.CreatedAt,
			&productIngredient.UpdatedAt,
			&product.ID,
			&product.OrganizationID,
			&product.CategoryID,
			&product.SKU,
			&product.Name,
			&product.Description,
			&product.Price,
			&product.Cost,
			&product.BusinessType,
			&product.ImageURL,
			&product.PrinterType,
			&product.UnitID,
			&product.HasIngredients,
			&product.Metadata,
			&product.IsActive,
			&product.CreatedAt,
			&product.UpdatedAt,
			&ingredient.ID,
			&ingredient.OrganizationID,
			&ingredient.OutletID,
			&ingredient.Name,
			&ingredient.UnitID,
			&ingredient.Cost,
			&ingredient.Stock,
			&ingredient.IsSemiFinished,
			&ingredient.IsActive,
			&ingredient.Metadata,
			&ingredient.CreatedAt,
			&ingredient.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}

		productIngredient.Product = product
		productIngredient.Ingredient = ingredient
		productIngredients = append(productIngredients, productIngredient)
	}

	return productIngredients, nil
}

func (r *ProductIngredientRepository) Update(ctx context.Context, productIngredient *entities.ProductIngredient) error {
	query := `
		UPDATE product_ingredients
		SET outlet_id = $1, quantity = $2, updated_at = $3
		WHERE id = $4 AND organization_id = $5
	`

	result, err := r.db.ExecContext(ctx, query,
		productIngredient.OutletID,
		productIngredient.Quantity,
		productIngredient.UpdatedAt,
		productIngredient.ID,
		productIngredient.OrganizationID,
	)

	if err != nil {
		return err
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if rowsAffected == 0 {
		return sql.ErrNoRows
	}

	return nil
}

func (r *ProductIngredientRepository) Delete(ctx context.Context, id, organizationID uuid.UUID) error {
	query := `DELETE FROM product_ingredients WHERE id = $1 AND organization_id = $2`

	result, err := r.db.ExecContext(ctx, query, id, organizationID)
	if err != nil {
		return err
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if rowsAffected == 0 {
		return sql.ErrNoRows
	}

	return nil
}

func (r *ProductIngredientRepository) DeleteByProductID(ctx context.Context, productID, organizationID uuid.UUID) error {
	query := `DELETE FROM product_ingredients WHERE product_id = $1 AND organization_id = $2`

	_, err := r.db.ExecContext(ctx, query, productID, organizationID)
	return err
}
