/**
 * useAiTransform - AI Validation Data Transformation
 *
 * Handles applying AI changes to row data with type coercion.
 *
 * PERFORMANCE NOTE: This hook must NOT subscribe to the rows array!
 * Subscribing to rows causes re-render on EVERY cell change.
 * Instead, use getState() to read rows at action time.
 */

import { useCallback } from 'react';
import { useValidationStore } from '../../store/validationStore';
import type { AiValidationChange, AiValidationResults, AiTokenUsage } from '../../store/types';
import type { Field, SelectOption } from '../../../../types';

/**
 * Helper to convert a value to number or null
 */
const toNumberOrNull = (value: unknown): number | null => {
  if (typeof value === 'number' && Number.isFinite(value)) return value;
  if (typeof value === 'string') {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : null;
  }
  return null;
};

/**
 * Normalize token usage from various API response formats
 * Different AI providers return token usage in different formats
 */
export const normalizeTokenUsage = (usageSource: unknown): AiTokenUsage | undefined => {
  if (!usageSource || typeof usageSource !== 'object') return undefined;

  const source = usageSource as Record<string, unknown>;

  // Handle various naming conventions from different APIs
  const promptTokensRaw =
    source.prompt ?? source.promptTokens ?? source.prompt_tokens ?? source.input;
  const completionTokensRaw =
    source.completion ?? source.completionTokens ?? source.completion_tokens ?? source.output;
  const totalTokensRaw =
    source.total ?? source.totalTokens ?? source.total_tokens ?? source.tokenTotal;
  const reasoningTokensRaw =
    source.reasoning ?? source.reasoningTokens ?? source.reasoning_tokens;
  const cachedPromptRaw =
    source.cachedPrompt ?? source.cachedTokens ?? source.cached_prompt ?? source.cached_tokens ?? source.cached;

  const prompt = toNumberOrNull(promptTokensRaw);
  const completion = toNumberOrNull(completionTokensRaw);
  let total = toNumberOrNull(totalTokensRaw);

  // Calculate total if not provided
  if (total === null && prompt !== null && completion !== null) {
    total = prompt + completion;
  }

  const reasoning = toNumberOrNull(reasoningTokensRaw);
  const cachedPrompt = toNumberOrNull(cachedPromptRaw);

  // Only return if we have at least some data
  if (prompt !== null || completion !== null || total !== null || reasoning !== null || cachedPrompt !== null) {
    return { prompt, completion, total, reasoning, cachedPrompt };
  }

  return undefined;
};

/**
 * Normalize reasoning effort level
 */
const normalizeReasoningEffort = (value: unknown): string | undefined => {
  if (typeof value !== 'string') return undefined;
  const normalized = value.toLowerCase();
  if (['minimal', 'low', 'medium', 'high'].includes(normalized)) {
    return normalized.charAt(0).toUpperCase() + normalized.slice(1);
  }
  return undefined;
};

/**
 * Coerce a value to match the expected field type
 */
const coerceValue = (value: unknown, field: Field<string>): unknown => {
  if (value === null || value === undefined) return value;

  const fieldType = field.fieldType.type;

  switch (fieldType) {
    case 'checkbox':
      // Convert to boolean
      if (typeof value === 'boolean') return value;
      if (typeof value === 'string') {
        const lower = value.toLowerCase();
        if (['true', 'yes', '1', 'on'].includes(lower)) return true;
        if (['false', 'no', '0', 'off'].includes(lower)) return false;
      }
      return Boolean(value);

    case 'multi-select':
    case 'multi-input':
      // Convert to array
      if (Array.isArray(value)) return value;
      if (typeof value === 'string') {
        const separator = field.fieldType.separator || ',';
        return value.split(separator).map((v) => v.trim()).filter(Boolean);
      }
      return [value];

    case 'select':
      // Ensure it's a valid option value
      if ('options' in field.fieldType) {
        const strValue = String(value);
        const validOption = field.fieldType.options.find(
          (opt: SelectOption) => opt.value === strValue || opt.label === strValue
        );
        return validOption?.value || strValue;
      }
      return String(value);

    case 'input':
      // Handle price fields
      if ('price' in field.fieldType && field.fieldType.price) {
        const strValue = String(value).replace(/[$,\s]/g, '');
        const num = parseFloat(strValue);
        return isNaN(num) ? value : num.toFixed(2);
      }
      return String(value);

    default:
      return value;
  }
};

/**
 * Hook for transforming and applying AI validation results
 * PERFORMANCE: Uses getState() to read rows/fields at action time
 */
export const useAiTransform = () => {
  // PERFORMANCE: Only get store actions, no state subscriptions
  const storeOriginalValues = useValidationStore((state) => state.storeOriginalValues);
  const setAiValidationResults = useValidationStore((state) => state.setAiValidationResults);

  /**
   * Apply AI changes to rows
   * PERFORMANCE: Uses getState() to read rows/fields at action time
   */
  const applyAiChanges = useCallback(
    (_aiProducts: Record<string, unknown>[], changes: AiValidationChange[]) => {
      // Get current state at action time
      const { rows, fields, updateRow } = useValidationStore.getState();

      // Store original values for revert functionality
      storeOriginalValues();

      // Group changes by product index for batch updates
      const changesByProduct = new Map<number, Record<string, unknown>>();

      changes.forEach((change) => {
        const productChanges = changesByProduct.get(change.productIndex) || {};
        const field = fields.find((f) => f.key === change.fieldKey);

        if (field) {
          productChanges[change.fieldKey] = coerceValue(change.correctedValue, field);
        } else {
          productChanges[change.fieldKey] = change.correctedValue;
        }

        changesByProduct.set(change.productIndex, productChanges);
      });

      // Apply changes to rows
      changesByProduct.forEach((productChanges, productIndex) => {
        if (productIndex >= 0 && productIndex < rows.length) {
          // Mark which fields were changed by AI
          const __changes: Record<string, boolean> = {};
          Object.keys(productChanges).forEach((key) => {
            __changes[key] = true;
          });

          updateRow(productIndex, {
            ...productChanges,
            __changes,
            __corrected: productChanges,
          });
        }
      });
    },
    [storeOriginalValues]
  );

  /**
   * Build results summary from AI response
   * PERFORMANCE: Uses getState() to read row count at action time
   */
  const buildResults = useCallback(
    (
      changes: AiValidationChange[],
      rawTokenUsage: unknown,
      processingTime: number,
      metadata?: {
        model?: string;
        reasoningEffort?: string;
        summary?: string;
        warnings?: string[];
        changesSummary?: string[];
      }
    ): AiValidationResults => {
      const { rows } = useValidationStore.getState();
      const affectedProducts = new Set(changes.map((c) => c.productIndex));

      // Normalize token usage from various formats
      const tokenUsage = normalizeTokenUsage(rawTokenUsage);

      return {
        totalProducts: rows.length,
        productsWithChanges: affectedProducts.size,
        changes,
        tokenUsage,
        processingTime,
        model: metadata?.model,
        reasoningEffort: normalizeReasoningEffort(metadata?.reasoningEffort),
        summary: metadata?.summary,
        warnings: metadata?.warnings,
        changesSummary: metadata?.changesSummary,
      };
    },
    []
  );

  /**
   * Set validation results in store
   */
  const saveResults = useCallback(
    (results: AiValidationResults) => {
      setAiValidationResults(results);
    },
    [setAiValidationResults]
  );

  return {
    applyAiChanges,
    buildResults,
    saveResults,
  };
};
