package fifo_stock_v2

import (
	"context"
	"fmt"
	"strings"

	"gorm.io/gorm"
)

type routeRule struct {
	ID                  uint    `gorm:"column:id"`
	FlagGroupCode       string  `gorm:"column:flag_group_code"`
	Lane                string  `gorm:"column:lane"`
	FunctionCode        string  `gorm:"column:function_code"`
	SourceTable         string  `gorm:"column:source_table"`
	SourceIDColumn      string  `gorm:"column:source_id_column"`
	ProductWarehouseCol string  `gorm:"column:product_warehouse_col"`
	QuantityCol         string  `gorm:"column:quantity_col"`
	UsedQuantityCol     *string `gorm:"column:used_quantity_col"`
	PendingQuantityCol  *string `gorm:"column:pending_quantity_col"`
	ScopeSQL            *string `gorm:"column:scope_sql"`
	LegacyTypeKey       string  `gorm:"column:legacy_type_key"`
	AllowPendingDefault bool    `gorm:"column:allow_pending_default"`
}

type traitRule struct {
	ID                 uint    `gorm:"column:id"`
	SourceTable        string  `gorm:"column:source_table"`
	Lane               string  `gorm:"column:lane"`
	DateTable          *string `gorm:"column:date_table"`
	DateJoinLeftCol    *string `gorm:"column:date_join_left_col"`
	DateJoinRightCol   *string `gorm:"column:date_join_right_col"`
	DateColumn         string  `gorm:"column:date_column"`
	FallbackDateColumn *string `gorm:"column:fallback_date_column"`
	SortPriority       int     `gorm:"column:sort_priority"`
	IDColumn           string  `gorm:"column:id_column"`
}

func (s *fifoStockV2Service) loadRouteRules(ctx context.Context, tx *gorm.DB, flagGroupCode string, lane Lane) ([]routeRule, error) {
	var rules []routeRule
	err := tx.WithContext(ctx).
		Table("fifo_stock_v2_route_rules").
		Where("is_active = TRUE").
		Where("flag_group_code = ?", flagGroupCode).
		Where("lane = ?", string(lane)).
		Order("id ASC").
		Find(&rules).Error
	if err != nil {
		return nil, err
	}
	for _, rule := range rules {
		if err := validateRouteRule(rule); err != nil {
			return nil, err
		}
	}
	return rules, nil
}

func (s *fifoStockV2Service) loadRouteRuleByLegacyType(
	ctx context.Context,
	tx *gorm.DB,
	lane Lane,
	flagGroupCode string,
	legacyTypeKey string,
) (*routeRule, error) {
	var rule routeRule
	err := tx.WithContext(ctx).
		Table("fifo_stock_v2_route_rules").
		Where("is_active = TRUE").
		Where("lane = ?", string(lane)).
		Where("flag_group_code = ?", flagGroupCode).
		Where("legacy_type_key = ?", legacyTypeKey).
		Order("id ASC").
		Limit(1).
		Take(&rule).Error
	if err != nil {
		return nil, err
	}
	if err := validateRouteRule(rule); err != nil {
		return nil, err
	}
	return &rule, nil
}

func (s *fifoStockV2Service) loadTraitMap(
	ctx context.Context,
	tx *gorm.DB,
	lane Lane,
	sourceTables []string,
) (map[string]traitRule, error) {
	if len(sourceTables) == 0 {
		return map[string]traitRule{}, nil
	}

	var traits []traitRule
	err := tx.WithContext(ctx).
		Table("fifo_stock_v2_traits").
		Where("is_active = TRUE").
		Where("lane = ?", string(lane)).
		Where("source_table IN ?", sourceTables).
		Find(&traits).Error
	if err != nil {
		return nil, err
	}

	out := make(map[string]traitRule, len(traits))
	for _, tr := range traits {
		if err := validateTraitRule(tr); err != nil {
			return nil, err
		}
		out[tr.SourceTable] = tr
	}
	return out, nil
}

func validateRouteRule(rule routeRule) error {
	fields := []string{rule.SourceTable, rule.SourceIDColumn, rule.ProductWarehouseCol, rule.QuantityCol}
	for _, value := range fields {
		if _, err := mustSafeIdentifier(value); err != nil {
			return err
		}
	}
	if rule.UsedQuantityCol != nil {
		if _, err := mustSafeIdentifier(*rule.UsedQuantityCol); err != nil {
			return err
		}
	}
	if rule.PendingQuantityCol != nil {
		if _, err := mustSafeIdentifier(*rule.PendingQuantityCol); err != nil {
			return err
		}
	}
	if strings.TrimSpace(rule.LegacyTypeKey) == "" {
		return fmt.Errorf("route rule has empty legacy type key")
	}
	return nil
}

func validateTraitRule(rule traitRule) error {
	if _, err := mustSafeIdentifier(rule.SourceTable); err != nil {
		return err
	}
	if _, err := mustSafeIdentifier(rule.DateColumn); err != nil {
		return err
	}
	if _, err := mustSafeIdentifier(rule.IDColumn); err != nil {
		return err
	}
	if rule.DateTable != nil {
		if _, err := mustSafeIdentifier(*rule.DateTable); err != nil {
			return err
		}
		if rule.DateJoinLeftCol == nil || rule.DateJoinRightCol == nil {
			return fmt.Errorf("trait %s requires date join columns", rule.SourceTable)
		}
		if _, err := mustSafeIdentifier(*rule.DateJoinLeftCol); err != nil {
			return err
		}
		if _, err := mustSafeIdentifier(*rule.DateJoinRightCol); err != nil {
			return err
		}
	}
	if rule.FallbackDateColumn != nil {
		if _, err := mustSafeIdentifier(*rule.FallbackDateColumn); err != nil {
			return err
		}
	}
	return nil
}
