package repository

import (
	"context"
	"database/sql"
	"fmt"

	"erp-mvp/core-service/internal/models"

	"github.com/google/uuid"
)

type ItemRepository interface {
	Create(ctx context.Context, item *models.Item) error
	GetByID(ctx context.Context, id uuid.UUID, orgID uuid.UUID) (*models.Item, error)
	GetByOrganization(ctx context.Context, orgID uuid.UUID) ([]*models.Item, error)
	Update(ctx context.Context, item *models.Item) error
	Delete(ctx context.Context, id uuid.UUID, orgID uuid.UUID) error
	Search(ctx context.Context, orgID uuid.UUID, query string, category string) ([]*models.Item, error)
}

type itemRepository struct {
	db *sql.DB
}

func NewItemRepository(db *sql.DB) ItemRepository {
	return &itemRepository{db: db}
}

func (r *itemRepository) Create(ctx context.Context, item *models.Item) error {
	query := `
		INSERT INTO items (id, organization_id, name, description, category, created_at)
		VALUES ($1, $2, $3, $4, $5, $6)
	`

	_, err := r.db.ExecContext(ctx, query,
		item.ID,
		item.OrganizationID,
		item.Name,
		item.Description,
		item.Category,
		item.CreatedAt,
	)
	if err != nil {
		return fmt.Errorf("failed to create item: %w", err)
	}

	return nil
}

func (r *itemRepository) GetByID(ctx context.Context, id uuid.UUID, orgID uuid.UUID) (*models.Item, error) {
	query := `
		SELECT id, organization_id, name, description, category, created_at
		FROM items
		WHERE id = $1 AND organization_id = $2
	`

	item := &models.Item{}
	err := r.db.QueryRowContext(ctx, query, id, orgID).Scan(
		&item.ID,
		&item.OrganizationID,
		&item.Name,
		&item.Description,
		&item.Category,
		&item.CreatedAt,
	)

	if err != nil {
		if err == sql.ErrNoRows {
			return nil, fmt.Errorf("item not found")
		}
		return nil, fmt.Errorf("failed to get item: %w", err)
	}

	return item, nil
}

func (r *itemRepository) GetByOrganization(ctx context.Context, orgID uuid.UUID) ([]*models.Item, error) {
	query := `
		SELECT id, organization_id, name, description, category, created_at
		FROM items
		WHERE organization_id = $1
		ORDER BY name
	`

	rows, err := r.db.QueryContext(ctx, query, orgID)
	if err != nil {
		return nil, fmt.Errorf("failed to query items: %w", err)
	}
	defer rows.Close()

	var items []*models.Item
	for rows.Next() {
		item := &models.Item{}
		err := rows.Scan(
			&item.ID,
			&item.OrganizationID,
			&item.Name,
			&item.Description,
			&item.Category,
			&item.CreatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan item: %w", err)
		}

		items = append(items, item)
	}

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

	return items, nil
}

func (r *itemRepository) Update(ctx context.Context, item *models.Item) error {
	query := `
		UPDATE items
		SET name = $3, description = $4, category = $5
		WHERE id = $1 AND organization_id = $2
	`

	result, err := r.db.ExecContext(ctx, query,
		item.ID,
		item.OrganizationID,
		item.Name,
		item.Description,
		item.Category,
	)
	if err != nil {
		return fmt.Errorf("failed to update item: %w", err)
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("failed to get rows affected: %w", err)
	}

	if rowsAffected == 0 {
		return fmt.Errorf("item not found")
	}

	return nil
}

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

	result, err := r.db.ExecContext(ctx, query, id, orgID)
	if err != nil {
		return fmt.Errorf("failed to delete item: %w", err)
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("failed to get rows affected: %w", err)
	}

	if rowsAffected == 0 {
		return fmt.Errorf("item not found")
	}

	return nil
}

func (r *itemRepository) Search(ctx context.Context, orgID uuid.UUID, query string, category string) ([]*models.Item, error) {
	baseQuery := `
		SELECT id, organization_id, name, description, category, created_at
		FROM items
		WHERE organization_id = $1
	`

	var args []interface{}
	args = append(args, orgID)
	argIndex := 2

	if query != "" {
		baseQuery += fmt.Sprintf(" AND (name ILIKE $%d OR description ILIKE $%d)", argIndex, argIndex)
		args = append(args, "%"+query+"%")
		argIndex++
	}

	if category != "" {
		baseQuery += fmt.Sprintf(" AND category = $%d", argIndex)
		args = append(args, category)
		argIndex++
	}

	baseQuery += " ORDER BY name"

	rows, err := r.db.QueryContext(ctx, baseQuery, args...)
	if err != nil {
		return nil, fmt.Errorf("failed to search items: %w", err)
	}
	defer rows.Close()

	var items []*models.Item
	for rows.Next() {
		item := &models.Item{}
		err := rows.Scan(
			&item.ID,
			&item.OrganizationID,
			&item.Name,
			&item.Description,
			&item.Category,
			&item.CreatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan item: %w", err)
		}

		items = append(items, item)
	}

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

	return items, nil
}
