package repositories

import (
	"context"
	"fmt"
	"strings"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"

	"gorm.io/gorm"
)

type PurchaseSupplierRepository interface {
	GetSuppliersWithPurchases(ctx context.Context, offset, limit int, filters *validation.PurchaseSupplierQuery) ([]entity.Supplier, int64, error)
	GetItemsBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.PurchaseSupplierQuery) ([]entity.PurchaseItem, error)
}

type purchaseSupplierRepositoryImpl struct {
	db *gorm.DB
}

func NewPurchaseSupplierRepository(db *gorm.DB) PurchaseSupplierRepository {
	return &purchaseSupplierRepositoryImpl{db: db}
}

func (r *purchaseSupplierRepositoryImpl) latestPurchaseApproval(ctx context.Context) *gorm.DB {
	return r.db.WithContext(ctx).
		Table("approvals AS a").
		Select("a.approvable_id, a.step_number, a.action").
		Joins(`
			JOIN (
				SELECT approvable_id, MAX(action_at) AS latest_action_at
				FROM approvals
				WHERE approvable_type = ?
				GROUP BY approvable_id
			) AS la ON la.approvable_id = a.approvable_id AND la.latest_action_at = a.action_at`,
			string(utils.ApprovalWorkflowPurchase),
		)
}

func (r *purchaseSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, filters *validation.PurchaseSupplierQuery) *gorm.DB {
	dateColumn := "purchase_items.received_date"
	switch strings.ToLower(strings.TrimSpace(filters.FilterBy)) {
	case "po_date":
		dateColumn = "purchases.po_date"
	case "receive_date", "":
		dateColumn = "purchase_items.received_date"
	}

	latestApproval := r.latestPurchaseApproval(ctx)

	db := r.db.WithContext(ctx).
		Model(&entity.Supplier{}).
		Joins("JOIN purchases ON purchases.supplier_id = suppliers.id").
		Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
		Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", latestApproval).
		Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
		Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
		Where("purchase_items.received_date IS NOT NULL")

	if len(filters.SupplierIDs) > 0 {
		db = db.Where("suppliers.id IN ?", filters.SupplierIDs)
	}

	if len(filters.ProductIDs) > 0 {
		db = db.Where("purchase_items.product_id IN ?", filters.ProductIDs)
	}

	if len(filters.ProductCategoryIDs) > 0 {
		db = db.
			Joins("JOIN products ON products.id = purchase_items.product_id").
			Where("products.product_category_id IN ?", filters.ProductCategoryIDs)
	}

	if len(filters.AreaIDs) > 0 || filters.AllowedAreaIDs != nil {
		db = db.Joins("JOIN warehouses ON warehouses.id = purchase_items.warehouse_id")
		if len(filters.AreaIDs) > 0 {
			db = db.Where("warehouses.area_id IN ?", filters.AreaIDs)
		}
		if filters.AllowedAreaIDs != nil {
			if len(filters.AllowedAreaIDs) == 0 {
				db = db.Where("1 = 0")
			} else {
				db = db.Where("warehouses.area_id IN ?", filters.AllowedAreaIDs)
			}
		}
	}

	if filters.StartDate != "" {
		if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
			db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom)
		}
	}

	if filters.EndDate != "" {
		if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil {
			db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo)
		}
	}

	return db
}

func (r *purchaseSupplierRepositoryImpl) GetSuppliersWithPurchases(ctx context.Context, offset, limit int, filters *validation.PurchaseSupplierQuery) ([]entity.Supplier, int64, error) {
	query := r.baseSupplierQuery(ctx, filters)

	var totalSuppliers int64
	if err := query.
		Distinct("suppliers.id").
		Count(&totalSuppliers).Error; err != nil {
		return nil, 0, err
	}

	if totalSuppliers == 0 {
		return []entity.Supplier{}, 0, nil
	}

	if offset < 0 {
		offset = 0
	}

	var supplierIDs []uint
	if err := query.
		Select("suppliers.id").
		Order("suppliers.id ASC").
		Offset(offset).
		Limit(limit).
		Pluck("suppliers.id", &supplierIDs).Error; err != nil {
		return nil, 0, err
	}

	if len(supplierIDs) == 0 {
		return []entity.Supplier{}, totalSuppliers, nil
	}

	var suppliers []entity.Supplier
	if err := r.db.WithContext(ctx).
		Where("id IN ?", supplierIDs).
		Find(&suppliers).Error; err != nil {
		return nil, 0, err
	}

	return suppliers, totalSuppliers, nil
}

func (r *purchaseSupplierRepositoryImpl) GetItemsBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.PurchaseSupplierQuery) ([]entity.PurchaseItem, error) {
	if len(supplierIDs) == 0 {
		return []entity.PurchaseItem{}, nil
	}

	// Tentukan kolom tanggal yang akan dipakai untuk filter & sort
	dateColumn := "purchase_items.received_date"
	switch strings.ToLower(strings.TrimSpace(filters.FilterBy)) {
	case "po_date":
		dateColumn = "purchases.po_date"
	case "receive_date", "":
		dateColumn = "purchase_items.received_date"
	}

	orderDirection := "ASC"
	switch strings.ToUpper(strings.TrimSpace(filters.SortBy)) {
	case "DESC":
		orderDirection = "DESC"
	case "ASC", "":
		orderDirection = "ASC"
	}

	db := r.db.WithContext(ctx).
		Model(&entity.PurchaseItem{}).
		Preload("Purchase").
		Preload("Purchase.Supplier").
		Preload("Product").
		Preload("Product.ProductCategory").
		Preload("Warehouse").
		Preload("Warehouse.Area").
		Preload("Warehouse.Location").
		Preload("Warehouse.Kandang").
		Preload("ExpenseNonstock").
		Preload("ExpenseNonstock.Expense").
		Preload("ExpenseNonstock.Expense.Supplier").
		Joins("JOIN purchases ON purchases.id = purchase_items.purchase_id").
		Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
		Where("purchases.supplier_id IN ?", supplierIDs).
		Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
		Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
		Where("purchase_items.received_date IS NOT NULL")

	if len(filters.ProductIDs) > 0 {
		db = db.Where("purchase_items.product_id IN ?", filters.ProductIDs)
	}

	if len(filters.ProductCategoryIDs) > 0 {
		db = db.
			Joins("JOIN products ON products.id = purchase_items.product_id").
			Where("products.product_category_id IN ?", filters.ProductCategoryIDs)
	}
	if len(filters.AreaIDs) > 0 || filters.AllowedAreaIDs != nil {
		db = db.Joins("JOIN warehouses ON warehouses.id = purchase_items.warehouse_id")
		if len(filters.AreaIDs) > 0 {
			db = db.Where("warehouses.area_id IN ?", filters.AreaIDs)
		}
		if filters.AllowedAreaIDs != nil {
			if len(filters.AllowedAreaIDs) == 0 {
				db = db.Where("1 = 0")
			} else {
				db = db.Where("warehouses.area_id IN ?", filters.AllowedAreaIDs)
			}
		}
	}

	if filters.StartDate != "" {
		if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
			db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom)
		}
	}

	if filters.EndDate != "" {
		if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil {
			db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo)
		}
	}

	// Urutkan berdasarkan kolom tanggal yang dipilih dan arah sort
	db = db.Order(fmt.Sprintf("%s %s", dateColumn, orderDirection)).
		Order("purchase_items.id ASC")

	var items []entity.PurchaseItem
	if err := db.Find(&items).Error; err != nil {
		return nil, err
	}

	return items, nil
}
