package service

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

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

const (
	transferLayingInFunctionCode = "TRANSFER_TO_LAYING_IN"
	transferLayingStockableLane  = "STOCKABLE"
	transferLayingSourceTable    = "laying_transfer_targets"

	transferLayingOutFunctionCode         = "TRANSFER_TO_LAYING_OUT"
	transferLayingUsableLane              = "USABLE"
	transferLayingUsableSourceTable       = "laying_transfers"
	transferLayingLegacyUsableSourceTable = "laying_transfer_sources"
)

func reflowTransferLayingScope(
	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 tx == nil {
		return fmt.Errorf("transaction is required")
	}
	if productWarehouseID == 0 {
		return fmt.Errorf("product warehouse id is required")
	}

	flagGroupCode, err := resolveTransferLayingFlagGroupByProductWarehouse(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,
	})
	return err
}

func resolveTransferLayingFlagGroupByProductWarehouse(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 = ?", transferLayingStockableLane).
		Where("rr.function_code = ?", transferLayingInFunctionCode).
		Where("rr.source_table = ?", transferLayingSourceTable).
		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 "", err
	}

	return strings.TrimSpace(selected.FlagGroupCode), nil
}

type transferLayingUsableRouteRule struct {
	FlagGroupCode string `gorm:"column:flag_group_code"`
	SourceTable   string `gorm:"column:source_table"`
}

func resolveTransferLayingUsableFlagGroupByProductWarehouse(ctx context.Context, tx *gorm.DB, productWarehouseID uint) (string, error) {
	rows := make([]transferLayingUsableRouteRule, 0)
	err := tx.WithContext(ctx).
		Table("fifo_stock_v2_route_rules rr").
		Select("rr.flag_group_code, rr.source_table").
		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 = ?", transferLayingUsableLane).
		Where("rr.function_code = ?", transferLayingOutFunctionCode).
		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").
		Find(&rows).Error
	if err != nil {
		return "", err
	}

	return validateTransferLayingUsableRouteRules(rows, productWarehouseID)
}

func validateTransferLayingUsableRouteRules(rows []transferLayingUsableRouteRule, productWarehouseID uint) (string, error) {
	if len(rows) == 0 {
		return "", fmt.Errorf(
			"konfigurasi FIFO v2 TRANSFER_TO_LAYING_OUT tidak ditemukan untuk source warehouse %d",
			productWarehouseID,
		)
	}

	var selectedFlagGroup string
	hasHeaderRule := false
	hasLegacyRule := false

	for _, row := range rows {
		sourceTable := strings.ToLower(strings.TrimSpace(row.SourceTable))
		flagGroupCode := strings.TrimSpace(row.FlagGroupCode)

		switch sourceTable {
		case transferLayingUsableSourceTable:
			if flagGroupCode == "" {
				return "", fmt.Errorf("konfigurasi FIFO v2 TRANSFER_TO_LAYING_OUT memiliki flag_group_code kosong")
			}
			hasHeaderRule = true
			if selectedFlagGroup == "" {
				selectedFlagGroup = flagGroupCode
				continue
			}
			if selectedFlagGroup != flagGroupCode {
				return "", fmt.Errorf(
					"konfigurasi FIFO v2 TRANSFER_TO_LAYING_OUT ambigu untuk source warehouse %d",
					productWarehouseID,
				)
			}
		case transferLayingLegacyUsableSourceTable:
			hasLegacyRule = true
		}
	}

	if hasLegacyRule {
		return "", fmt.Errorf(
			"konfigurasi FIFO v2 legacy untuk TRANSFER_TO_LAYING_OUT masih aktif (source_table=%s)",
			transferLayingLegacyUsableSourceTable,
		)
	}
	if !hasHeaderRule {
		return "", fmt.Errorf(
			"konfigurasi FIFO v2 TRANSFER_TO_LAYING_OUT aktif untuk source_table=%s tidak ditemukan",
			transferLayingUsableSourceTable,
		)
	}

	return selectedFlagGroup, nil
}
