package repository

import (
	"context"
	"strings"
	"time"

	"github.com/google/uuid"

	"apskel-pos-be/internal/entities"

	"gorm.io/gorm"
)

type PurchaseOrderRepositoryImpl struct {
	db *gorm.DB
}

func NewPurchaseOrderRepositoryImpl(db *gorm.DB) *PurchaseOrderRepositoryImpl {
	return &PurchaseOrderRepositoryImpl{
		db: db,
	}
}

func (r *PurchaseOrderRepositoryImpl) Create(ctx context.Context, po *entities.PurchaseOrder) error {
	return r.db.WithContext(ctx).Create(po).Error
}

func (r *PurchaseOrderRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*entities.PurchaseOrder, error) {
	var po entities.PurchaseOrder
	err := r.db.WithContext(ctx).
		Preload("Vendor").
		Preload("Items.Ingredient").
		Preload("Items.Unit").
		Preload("Attachments.File").
		First(&po, "id = ?", id).Error
	if err != nil {
		return nil, err
	}
	return &po, nil
}

func (r *PurchaseOrderRepositoryImpl) GetByIDAndOrganizationID(ctx context.Context, id, organizationID uuid.UUID) (*entities.PurchaseOrder, error) {
	var po entities.PurchaseOrder
	err := r.db.WithContext(ctx).
		Preload("Vendor").
		Preload("Items.Ingredient").
		Preload("Items.Unit").
		Preload("Attachments.File").
		Where("id = ? AND organization_id = ?", id, organizationID).
		First(&po).Error
	if err != nil {
		return nil, err
	}
	return &po, nil
}

func (r *PurchaseOrderRepositoryImpl) Update(ctx context.Context, po *entities.PurchaseOrder) error {
	return r.db.WithContext(ctx).Save(po).Error
}

func (r *PurchaseOrderRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.PurchaseOrder{}, "id = ?", id).Error
}

func (r *PurchaseOrderRepositoryImpl) List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, limit, offset int) ([]*entities.PurchaseOrder, int64, error) {
	var pos []*entities.PurchaseOrder
	var total int64

	query := r.db.WithContext(ctx).Model(&entities.PurchaseOrder{}).Where("organization_id = ?", organizationID)

	// Apply filters
	for key, value := range filters {
		switch key {
		case "search":
			if searchStr, ok := value.(string); ok && searchStr != "" {
				searchPattern := "%" + strings.ToLower(searchStr) + "%"
				query = query.Where("LOWER(po_number) LIKE ? OR LOWER(reference) LIKE ?", searchPattern, searchPattern)
			}
		case "status":
			if status, ok := value.(string); ok && status != "" {
				query = query.Where("status = ?", status)
			}
		case "vendor_id":
			if vendorID, ok := value.(uuid.UUID); ok {
				query = query.Where("vendor_id = ?", vendorID)
			}
		case "start_date":
			if startDate, ok := value.(time.Time); ok {
				query = query.Where("transaction_date >= ?", startDate)
			}
		case "end_date":
			if endDate, ok := value.(time.Time); ok {
				query = query.Where("transaction_date <= ?", endDate)
			}
		default:
			query = query.Where(key+" = ?", value)
		}
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	err := query.
		Preload("Vendor").
		Preload("Items.Ingredient").
		Preload("Items.Unit").
		Preload("Attachments.File").
		Order("created_at DESC").
		Limit(limit).
		Offset(offset).
		Find(&pos).Error
	return pos, total, err
}

func (r *PurchaseOrderRepositoryImpl) Count(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}) (int64, error) {
	var count int64
	query := r.db.WithContext(ctx).Model(&entities.PurchaseOrder{}).Where("organization_id = ?", organizationID)

	// Apply filters
	for key, value := range filters {
		switch key {
		case "search":
			if searchStr, ok := value.(string); ok && searchStr != "" {
				searchPattern := "%" + strings.ToLower(searchStr) + "%"
				query = query.Where("LOWER(po_number) LIKE ? OR LOWER(reference) LIKE ?", searchPattern, searchPattern)
			}
		case "status":
			if status, ok := value.(string); ok && status != "" {
				query = query.Where("status = ?", status)
			}
		case "vendor_id":
			if vendorID, ok := value.(uuid.UUID); ok {
				query = query.Where("vendor_id = ?", vendorID)
			}
		case "start_date":
			if startDate, ok := value.(time.Time); ok {
				query = query.Where("transaction_date >= ?", startDate)
			}
		case "end_date":
			if endDate, ok := value.(time.Time); ok {
				query = query.Where("transaction_date <= ?", endDate)
			}
		default:
			query = query.Where(key+" = ?", value)
		}
	}

	err := query.Count(&count).Error
	return count, err
}

func (r *PurchaseOrderRepositoryImpl) GetByPONumber(ctx context.Context, poNumber string, organizationID uuid.UUID) (*entities.PurchaseOrder, error) {
	var po entities.PurchaseOrder
	err := r.db.WithContext(ctx).
		Where("po_number = ? AND organization_id = ?", poNumber, organizationID).
		First(&po).Error
	if err != nil {
		return nil, err
	}
	return &po, nil
}

func (r *PurchaseOrderRepositoryImpl) GetByStatus(ctx context.Context, organizationID uuid.UUID, status string) ([]*entities.PurchaseOrder, error) {
	var pos []*entities.PurchaseOrder
	err := r.db.WithContext(ctx).
		Where("organization_id = ? AND status = ?", organizationID, status).
		Preload("Vendor").
		Preload("Items.Ingredient").
		Preload("Items.Unit").
		Find(&pos).Error
	return pos, err
}

func (r *PurchaseOrderRepositoryImpl) GetOverdue(ctx context.Context, organizationID uuid.UUID) ([]*entities.PurchaseOrder, error) {
	var pos []*entities.PurchaseOrder
	err := r.db.WithContext(ctx).
		Where("organization_id = ? AND due_date < ? AND status IN (?)", organizationID, time.Now(), []string{"draft", "sent", "approved"}).
		Preload("Vendor").
		Preload("Items.Ingredient").
		Preload("Items.Unit").
		Find(&pos).Error
	return pos, err
}

func (r *PurchaseOrderRepositoryImpl) UpdateStatus(ctx context.Context, id uuid.UUID, status string) error {
	return r.db.WithContext(ctx).
		Model(&entities.PurchaseOrder{}).
		Where("id = ?", id).
		Update("status", status).Error
}

func (r *PurchaseOrderRepositoryImpl) UpdateTotalAmount(ctx context.Context, id uuid.UUID, totalAmount float64) error {
	return r.db.WithContext(ctx).
		Model(&entities.PurchaseOrder{}).
		Where("id = ?", id).
		Update("total_amount", totalAmount).Error
}

// Purchase Order Items methods
func (r *PurchaseOrderRepositoryImpl) CreateItem(ctx context.Context, item *entities.PurchaseOrderItem) error {
	return r.db.WithContext(ctx).Create(item).Error
}

func (r *PurchaseOrderRepositoryImpl) UpdateItem(ctx context.Context, item *entities.PurchaseOrderItem) error {
	return r.db.WithContext(ctx).Save(item).Error
}

func (r *PurchaseOrderRepositoryImpl) DeleteItem(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.PurchaseOrderItem{}, "id = ?", id).Error
}

func (r *PurchaseOrderRepositoryImpl) DeleteItemsByPurchaseOrderID(ctx context.Context, purchaseOrderID uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.PurchaseOrderItem{}, "purchase_order_id = ?", purchaseOrderID).Error
}

func (r *PurchaseOrderRepositoryImpl) GetItemsByPurchaseOrderID(ctx context.Context, purchaseOrderID uuid.UUID) ([]*entities.PurchaseOrderItem, error) {
	var items []*entities.PurchaseOrderItem
	err := r.db.WithContext(ctx).
		Preload("Ingredient").
		Preload("Unit").
		Where("purchase_order_id = ?", purchaseOrderID).
		Find(&items).Error
	return items, err
}

// Purchase Order Attachments methods
func (r *PurchaseOrderRepositoryImpl) CreateAttachment(ctx context.Context, attachment *entities.PurchaseOrderAttachment) error {
	return r.db.WithContext(ctx).Create(attachment).Error
}

func (r *PurchaseOrderRepositoryImpl) DeleteAttachment(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.PurchaseOrderAttachment{}, "id = ?", id).Error
}

func (r *PurchaseOrderRepositoryImpl) DeleteAttachmentsByPurchaseOrderID(ctx context.Context, purchaseOrderID uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.PurchaseOrderAttachment{}, "purchase_order_id = ?", purchaseOrderID).Error
}

func (r *PurchaseOrderRepositoryImpl) GetAttachmentsByPurchaseOrderID(ctx context.Context, purchaseOrderID uuid.UUID) ([]*entities.PurchaseOrderAttachment, error) {
	var attachments []*entities.PurchaseOrderAttachment
	err := r.db.WithContext(ctx).
		Preload("File").
		Where("purchase_order_id = ?", purchaseOrderID).
		Find(&attachments).Error
	return attachments, err
}
