package service

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"
	"gorm.io/gorm"
)

const (
	purchaseInFunctionCode = "PURCHASE_IN"
	purchaseStockableLane  = "STOCKABLE"
	purchaseSourceTable    = "purchase_items"
)

func reflowPurchaseScope(
	ctx context.Context,
	fifoStockV2Svc commonSvc.FifoStockV2Service,
	tx *gorm.DB,
	productWarehouseID uint,
	asOf *time.Time,
) error {
	if fifoStockV2Svc == nil {
		return fmt.Errorf("FIFO v2 service is not available")
	}
	if productWarehouseID == 0 {
		return fmt.Errorf("product warehouse id is required")
	}

	flagGroupCode, err := resolvePurchaseFlagGroupByProductWarehouse(ctx, tx, productWarehouseID)
	if err != nil {
		return err
	}
	if strings.TrimSpace(flagGroupCode) == "" {
		return fmt.Errorf("flag group code is not found for product warehouse %d", productWarehouseID)
	}

	_, err = fifoStockV2Svc.Reflow(ctx, commonSvc.FifoStockV2ReflowRequest{
		FlagGroupCode:      flagGroupCode,
		ProductWarehouseID: productWarehouseID,
		AsOf:               asOf,
		Tx:                 tx,
	})
	if err != nil {
		return err
	}

	_, err = fifoStockV2Svc.Recalculate(ctx, commonSvc.FifoStockV2RecalculateRequest{
		ProductWarehouseIDs: []uint{productWarehouseID},
		FlagGroupCodes:      []string{flagGroupCode},
		AsOf:                asOf,
		FixDrift:            true,
		Tx:                  tx,
	})
	return err
}

func resolvePurchaseFlagGroupByProductWarehouse(ctx context.Context, tx *gorm.DB, productWarehouseID uint) (string, error) {
	type row struct {
		FlagGroupCode string `gorm:"column:flag_group_code"`
	}

	var selected row
	err := tx.WithContext(ctx).
		Table("fifo_stock_v2_route_rules rr").
		Select("rr.flag_group_code").
		Joins("JOIN fifo_stock_v2_flag_groups fg ON fg.code = rr.flag_group_code AND fg.is_active = TRUE").
		Where("rr.is_active = TRUE").
		Where("rr.lane = ?", purchaseStockableLane).
		Where("rr.function_code = ?", purchaseInFunctionCode).
		Where("rr.source_table = ?", purchaseSourceTable).
		Where(`
			EXISTS (
				SELECT 1
				FROM product_warehouses pw
				JOIN flags f ON f.flagable_id = pw.product_id
				JOIN fifo_stock_v2_flag_members fm ON fm.flag_name = f.name AND fm.is_active = TRUE
				WHERE pw.id = ?
				  AND f.flagable_type = ?
				  AND fm.flag_group_code = rr.flag_group_code
			)
		`, productWarehouseID, entity.FlagableTypeProduct).
		Order("rr.id ASC").
		Limit(1).
		Take(&selected).Error
	if err == nil {
		return strings.TrimSpace(selected.FlagGroupCode), nil
	}
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return "", err
	}

	type categoryRow struct {
		CategoryCode string `gorm:"column:category_code"`
	}

	var category categoryRow
	err = tx.WithContext(ctx).
		Table("product_warehouses pw").
		Select("pc.code AS category_code").
		Joins("JOIN products p ON p.id = pw.product_id").
		Joins("JOIN product_categories pc ON pc.id = p.product_category_id").
		Where("pw.id = ?", productWarehouseID).
		Limit(1).
		Take(&category).Error
	if err != nil {
		return "", err
	}

	flagGroupCode := utils.LegacyFlagGroupCodeByProductCategoryCode(category.CategoryCode)
	if flagGroupCode == "" {
		return "", gorm.ErrRecordNotFound
	}

	var matched int64
	err = tx.WithContext(ctx).
		Table("fifo_stock_v2_route_rules rr").
		Joins("JOIN fifo_stock_v2_flag_groups fg ON fg.code = rr.flag_group_code AND fg.is_active = TRUE").
		Where("rr.is_active = TRUE").
		Where("rr.lane = ?", purchaseStockableLane).
		Where("rr.function_code = ?", purchaseInFunctionCode).
		Where("rr.source_table = ?", purchaseSourceTable).
		Where("rr.flag_group_code = ?", flagGroupCode).
		Count(&matched).Error
	if err != nil {
		return "", err
	}
	if matched == 0 {
		return "", gorm.ErrRecordNotFound
	}

	return flagGroupCode, nil
}

func assignEarliestAsOf(m map[uint]time.Time, productWarehouseID uint, asOf time.Time) {
	if productWarehouseID == 0 {
		return
	}
	if current, ok := m[productWarehouseID]; !ok || asOf.Before(current) {
		m[productWarehouseID] = asOf
	}
}
