package repository

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

	"github.com/google/uuid"
)

type IngredientCompositionRepository struct {
	db *sql.DB
}

func NewIngredientCompositionRepository(db *sql.DB) *IngredientCompositionRepository {
	return &IngredientCompositionRepository{db: db}
}

func (r *IngredientCompositionRepository) Create(ctx context.Context, composition *entities.IngredientComposition) error {
	query := `
		INSERT INTO ingredient_compositions (id, organization_id, outlet_id, parent_ingredient_id, child_ingredient_id, quantity, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
	`

	_, err := r.db.ExecContext(ctx, query,
		composition.ID,
		composition.OrganizationID,
		composition.OutletID,
		composition.ParentIngredientID,
		composition.ChildIngredientID,
		composition.Quantity,
		composition.CreatedAt,
		composition.UpdatedAt,
	)

	return err
}

func (r *IngredientCompositionRepository) GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.IngredientComposition, error) {
	query := `
		SELECT ic.id, ic.organization_id, ic.outlet_id, ic.parent_ingredient_id, ic.child_ingredient_id, ic.quantity, ic.created_at, ic.updated_at,
		       pi.id, pi.organization_id, pi.outlet_id, pi.name, pi.unit_id, pi.cost, pi.stock, pi.is_semi_finished, pi.is_active, pi.metadata, pi.created_at, pi.updated_at,
		       ci.id, ci.organization_id, ci.outlet_id, ci.name, ci.unit_id, ci.cost, ci.stock, ci.is_semi_finished, ci.is_active, ci.metadata, ci.created_at, ci.updated_at
		FROM ingredient_compositions ic
		LEFT JOIN ingredients pi ON ic.parent_ingredient_id = pi.id
		LEFT JOIN ingredients ci ON ic.child_ingredient_id = ci.id
		WHERE ic.id = $1 AND ic.organization_id = $2
	`

	composition := &entities.IngredientComposition{}
	parentIngredient := &entities.Ingredient{}
	childIngredient := &entities.Ingredient{}

	err := r.db.QueryRowContext(ctx, query, id, organizationID).Scan(
		&composition.ID,
		&composition.OrganizationID,
		&composition.OutletID,
		&composition.ParentIngredientID,
		&composition.ChildIngredientID,
		&composition.Quantity,
		&composition.CreatedAt,
		&composition.UpdatedAt,
		&parentIngredient.ID,
		&parentIngredient.OrganizationID,
		&parentIngredient.OutletID,
		&parentIngredient.Name,
		&parentIngredient.UnitID,
		&parentIngredient.Cost,
		&parentIngredient.Stock,
		&parentIngredient.IsSemiFinished,
		&parentIngredient.IsActive,
		&parentIngredient.Metadata,
		&parentIngredient.CreatedAt,
		&parentIngredient.UpdatedAt,
		&childIngredient.ID,
		&childIngredient.OrganizationID,
		&childIngredient.OutletID,
		&childIngredient.Name,
		&childIngredient.UnitID,
		&childIngredient.Cost,
		&childIngredient.Stock,
		&childIngredient.IsSemiFinished,
		&childIngredient.IsActive,
		&childIngredient.Metadata,
		&childIngredient.CreatedAt,
		&childIngredient.UpdatedAt,
	)

	if err != nil {
		return nil, err
	}

	composition.ParentIngredient = parentIngredient
	composition.ChildIngredient = childIngredient
	return composition, nil
}

func (r *IngredientCompositionRepository) GetByParentIngredientID(ctx context.Context, parentIngredientID, organizationID uuid.UUID) ([]*entities.IngredientComposition, error) {
	query := `
		SELECT ic.id, ic.organization_id, ic.outlet_id, ic.parent_ingredient_id, ic.child_ingredient_id, ic.quantity, ic.created_at, ic.updated_at,
		       pi.id, pi.organization_id, pi.outlet_id, pi.name, pi.unit_id, pi.cost, pi.stock, pi.is_semi_finished, pi.is_active, pi.metadata, pi.created_at, pi.updated_at,
		       ci.id, ci.organization_id, ci.outlet_id, ci.name, ci.unit_id, ci.cost, ci.stock, ci.is_semi_finished, ci.is_active, ci.metadata, ci.created_at, ci.updated_at
		FROM ingredient_compositions ic
		LEFT JOIN ingredients pi ON ic.parent_ingredient_id = pi.id
		LEFT JOIN ingredients ci ON ic.child_ingredient_id = ci.id
		WHERE ic.parent_ingredient_id = $1 AND ic.organization_id = $2
		ORDER BY ic.created_at DESC
	`

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

	var compositions []*entities.IngredientComposition
	for rows.Next() {
		composition := &entities.IngredientComposition{}
		parentIngredient := &entities.Ingredient{}
		childIngredient := &entities.Ingredient{}

		err := rows.Scan(
			&composition.ID,
			&composition.OrganizationID,
			&composition.OutletID,
			&composition.ParentIngredientID,
			&composition.ChildIngredientID,
			&composition.Quantity,
			&composition.CreatedAt,
			&composition.UpdatedAt,
			&parentIngredient.ID,
			&parentIngredient.OrganizationID,
			&parentIngredient.OutletID,
			&parentIngredient.Name,
			&parentIngredient.UnitID,
			&parentIngredient.Cost,
			&parentIngredient.Stock,
			&parentIngredient.IsSemiFinished,
			&parentIngredient.IsActive,
			&parentIngredient.Metadata,
			&parentIngredient.CreatedAt,
			&parentIngredient.UpdatedAt,
			&childIngredient.ID,
			&childIngredient.OrganizationID,
			&childIngredient.OutletID,
			&childIngredient.Name,
			&childIngredient.UnitID,
			&childIngredient.Cost,
			&childIngredient.Stock,
			&childIngredient.IsSemiFinished,
			&childIngredient.IsActive,
			&childIngredient.Metadata,
			&childIngredient.CreatedAt,
			&childIngredient.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}

		composition.ParentIngredient = parentIngredient
		composition.ChildIngredient = childIngredient
		compositions = append(compositions, composition)
	}

	return compositions, nil
}

func (r *IngredientCompositionRepository) GetByChildIngredientID(ctx context.Context, childIngredientID, organizationID uuid.UUID) ([]*entities.IngredientComposition, error) {
	query := `
		SELECT ic.id, ic.organization_id, ic.outlet_id, ic.parent_ingredient_id, ic.child_ingredient_id, ic.quantity, ic.created_at, ic.updated_at,
		       pi.id, pi.organization_id, pi.outlet_id, pi.name, pi.unit_id, pi.cost, pi.stock, pi.is_semi_finished, pi.is_active, pi.metadata, pi.created_at, pi.updated_at,
		       ci.id, ci.organization_id, ci.outlet_id, ci.name, ci.unit_id, ci.cost, ci.stock, ci.is_semi_finished, ci.is_active, ci.metadata, ci.created_at, ci.updated_at
		FROM ingredient_compositions ic
		LEFT JOIN ingredients pi ON ic.parent_ingredient_id = pi.id
		LEFT JOIN ingredients ci ON ic.child_ingredient_id = ci.id
		WHERE ic.child_ingredient_id = $1 AND ic.organization_id = $2
		ORDER BY ic.created_at DESC
	`

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

	var compositions []*entities.IngredientComposition
	for rows.Next() {
		composition := &entities.IngredientComposition{}
		parentIngredient := &entities.Ingredient{}
		childIngredient := &entities.Ingredient{}

		err := rows.Scan(
			&composition.ID,
			&composition.OrganizationID,
			&composition.OutletID,
			&composition.ParentIngredientID,
			&composition.ChildIngredientID,
			&composition.Quantity,
			&composition.CreatedAt,
			&composition.UpdatedAt,
			&parentIngredient.ID,
			&parentIngredient.OrganizationID,
			&parentIngredient.OutletID,
			&parentIngredient.Name,
			&parentIngredient.UnitID,
			&parentIngredient.Cost,
			&parentIngredient.Stock,
			&parentIngredient.IsSemiFinished,
			&parentIngredient.IsActive,
			&parentIngredient.Metadata,
			&parentIngredient.CreatedAt,
			&parentIngredient.UpdatedAt,
			&childIngredient.ID,
			&childIngredient.OrganizationID,
			&childIngredient.OutletID,
			&childIngredient.Name,
			&childIngredient.UnitID,
			&childIngredient.Cost,
			&childIngredient.Stock,
			&childIngredient.IsSemiFinished,
			&childIngredient.IsActive,
			&childIngredient.Metadata,
			&childIngredient.CreatedAt,
			&childIngredient.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}

		composition.ParentIngredient = parentIngredient
		composition.ChildIngredient = childIngredient
		compositions = append(compositions, composition)
	}

	return compositions, nil
}

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

	result, err := r.db.ExecContext(ctx, query,
		composition.OutletID,
		composition.Quantity,
		composition.UpdatedAt,
		composition.ID,
		composition.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 *IngredientCompositionRepository) Delete(ctx context.Context, id, organizationID uuid.UUID) error {
	query := `DELETE FROM ingredient_compositions 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
}
