package repository

import (
	"apskel-pos-be/internal/entities"
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type TableRepository struct {
	db *gorm.DB
}

func NewTableRepository(db *gorm.DB) *TableRepository {
	return &TableRepository{db: db}
}

func (r *TableRepository) Create(ctx context.Context, table *entities.Table) error {
	return r.db.WithContext(ctx).Create(table).Error
}

func (r *TableRepository) GetByID(ctx context.Context, id uuid.UUID) (*entities.Table, error) {
	var table entities.Table
	err := r.db.WithContext(ctx).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Where("id = ?", id).
		First(&table).Error
	if err != nil {
		return nil, err
	}
	return &table, nil
}

func (r *TableRepository) GetByOutletID(ctx context.Context, outletID uuid.UUID) ([]entities.Table, error) {
	var tables []entities.Table
	err := r.db.WithContext(ctx).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Where("outlet_id = ?", outletID).
		Order("table_name").
		Find(&tables).Error
	return tables, err
}

func (r *TableRepository) GetByOrganizationID(ctx context.Context, organizationID uuid.UUID) ([]entities.Table, error) {
	var tables []entities.Table
	err := r.db.WithContext(ctx).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Where("organization_id = ?", organizationID).
		Order("table_name").
		Find(&tables).Error
	return tables, err
}

func (r *TableRepository) Update(ctx context.Context, table *entities.Table) error {
	return r.db.WithContext(ctx).Save(table).Error
}

func (r *TableRepository) Delete(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.Table{}, id).Error
}

func (r *TableRepository) List(ctx context.Context, organizationID, outletID *uuid.UUID, status *string, isActive *bool, search string, page, limit int) ([]entities.Table, int64, error) {
	var tables []entities.Table
	var total int64

	query := r.db.WithContext(ctx).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order")

	if organizationID != nil {
		query = query.Where("organization_id = ?", *organizationID)
	}

	if outletID != nil {
		query = query.Where("outlet_id = ?", *outletID)
	}

	if status != nil {
		query = query.Where("status = ?", *status)
	}

	if isActive != nil {
		query = query.Where("is_active = ?", *isActive)
	}

	if search != "" {
		searchTerm := fmt.Sprintf("%%%s%%", search)
		query = query.Where("table_name ILIKE ?", searchTerm)
	}

	// Count total
	err := query.Model(&entities.Table{}).Count(&total).Error
	if err != nil {
		return nil, 0, err
	}

	// Get paginated results
	offset := (page - 1) * limit
	err = query.Offset(offset).Limit(limit).Order("table_name").Find(&tables).Error

	return tables, total, err
}

func (r *TableRepository) GetAvailableTables(ctx context.Context, outletID uuid.UUID) ([]entities.Table, error) {
	var tables []entities.Table
	err := r.db.WithContext(ctx).
		Preload("Organization").
		Preload("Outlet").
		Where("outlet_id = ? AND status = ? AND is_active = ?", outletID, "available", true).
		Order("table_name").
		Find(&tables).Error
	return tables, err
}

func (r *TableRepository) GetOccupiedTables(ctx context.Context, outletID uuid.UUID) ([]entities.Table, error) {
	var tables []entities.Table
	err := r.db.WithContext(ctx).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Where("outlet_id = ? AND status = ? AND is_active = ?", outletID, "occupied", true).
		Order("table_name").
		Find(&tables).Error
	return tables, err
}

func (r *TableRepository) OccupyTable(ctx context.Context, tableID, orderID uuid.UUID, startTime *time.Time) error {
	return r.db.WithContext(ctx).
		Model(&entities.Table{}).
		Where("id = ?", tableID).
		Updates(map[string]interface{}{
			"status":     "occupied",
			"order_id":   orderID,
			"start_time": startTime,
		}).Error
}

func (r *TableRepository) ReleaseTable(ctx context.Context, tableID uuid.UUID, paymentAmount float64) error {
	return r.db.WithContext(ctx).
		Model(&entities.Table{}).
		Where("id = ?", tableID).
		Updates(map[string]interface{}{
			"status":         "available",
			"order_id":       nil,
			"start_time":     nil,
			"payment_amount": paymentAmount,
		}).Error
}

func (r *TableRepository) GetByOrderID(ctx context.Context, orderID uuid.UUID) (*entities.Table, error) {
	var table entities.Table
	err := r.db.WithContext(ctx).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Where("order_id = ?", orderID).
		First(&table).Error
	if err != nil {
		return nil, err
	}
	return &table, nil
}
