// This feature is only for debug mode (Castle Mode).
// This file was not reviewed by a human reviewer and must not become a part of production experience.

import { useAlert } from '@/aesthetics/Alert';
import { Text } from '@/aesthetics/Text';
import { useColors } from '@/aesthetics/styles';
import { useStrings } from '@/common/hooks/useStrings';
import { resizeAndEncodeImage } from '@/common/utils/image';
import { NUTRIENT_META } from '@/common/constants/nutrition';
import { ClaudeProvider, GeminiProvider, OpenAIProvider } from '@/add-log/food-detection/providers';
import type { FoodLogPred } from '@/types/foodlog';
import type { NutrientKey } from '@/types/nutrition';
import type { Strings } from '@/common/strings';
import { useMemo, useState } from 'react';
import { StyleSheet, View } from 'react-native';

type AIProviderName = 'claude' | 'gemini' | 'openai';

type FoodPrediction = {
  provider: AIProviderName;
  result?: { foodName: string; ingredients: string[]; nutrition?: Record<string, number> };
};

const claudeProvider = new ClaudeProvider();
const geminiProvider = new GeminiProvider();
const openaiProvider = new OpenAIProvider();

const PROVIDER_INSTANCES = {
  claude: claudeProvider,
  gemini: geminiProvider,
  openai: openaiProvider,
} as const;

async function askJudge(
  provider: AIProviderName,
  input: { base64?: string; prompt: string }
): Promise<string> {
  const result = await PROVIDER_INSTANCES[provider].query(input.prompt, input.base64);
  return result.trim();
}

export type JudgeResult = {
  judge: AIProviderName;
  selectedProvider: AIProviderName | 'unknown';
  reasoning: string;
  report: string;
};

const JUDGE_REPORT_WORDS = 50;

const trimToWordCount = (text: string, wordCount: number) => {
  const words = text.split(/\s+/).filter(Boolean);
  if (words.length <= wordCount) return words.join(' ');
  return words.slice(0, wordCount).join(' ');
};

const buildFallbackJudgeReport = (
  selectedProvider: AIProviderName | 'unknown',
  candidates: FoodPrediction[],
  strings: Strings
) => {
  const selected = candidates.find(c => c.provider === selectedProvider);
  const selectedName = selected?.result?.foodName ?? strings.seeLog.castle.theSelectedOption;
  const ingredients =
    selected?.result?.ingredients && selected.result.ingredients.length > 0
      ? ` and its listed ingredients (${selected.result.ingredients.slice(0, 3).join(', ')})`
      : '';
  const otherNames = candidates
    .filter(c => c.provider !== selectedProvider)
    .map(c => c.result?.foodName)
    .filter(Boolean);
  const otherList =
    otherNames.length > 0 ? otherNames.join(', ') : strings.seeLog.castle.theOtherOptions;

  return strings.seeLog.castle.fallbackReport(selectedName, ingredients, otherList);
};

const normalizeJudgeReport = (report: string, fallback: string) => {
  let text = report.trim();
  if (!text) {
    text = fallback;
  } else if (text.split(/\s+/).filter(Boolean).length < JUDGE_REPORT_WORDS) {
    text = `${text} ${fallback}`;
  }
  return trimToWordCount(text, JUDGE_REPORT_WORDS);
};

const parseJudgeResponse = (text: string) => {
  const winnerMatch =
    text.match(/Winner:\s*(\d+)/i) || text.match(/Option\s*(\d+)/i) || text.match(/\b(\d)\b/);
  const reportMatch = text.match(/Report:\s*([\s\S]+)/i);
  return {
    winnerIndex: winnerMatch ? parseInt(winnerMatch[1], 10) - 1 : null,
    report: reportMatch ? reportMatch[1].trim() : '',
  };
};

async function judgePredictions(
  base64: string,
  predictions: FoodPrediction[],
  strings: Strings
): Promise<JudgeResult[]> {
  const candidates = predictions.filter(p => p.result);
  if (candidates.length < 2) return [];

  const candidateDescriptions = candidates
    .map((c, i) =>
      strings.seeLog.castle.optionTemplate(
        i + 1,
        c.result?.foodName ?? '',
        c.result?.ingredients.join(', ') ?? ''
      )
    )
    .join('\n\n');

  const prompt = strings.ai.judgePredictions(candidates.length, candidateDescriptions);

  const input = { base64, prompt };

  const results = await Promise.allSettled([
    askJudge('claude', input),
    askJudge('gemini', input),
    askJudge('openai', input),
  ]);

  return results.map((res, i) => {
    const judge = PROVIDERS[i];
    if (res.status === 'rejected') {
      const fallback = buildFallbackJudgeReport('unknown', candidates, strings);
      return {
        judge,
        selectedProvider: 'unknown' as const,
        reasoning: strings.seeLog.castle.failedToJudge,
        report: trimToWordCount(fallback, JUDGE_REPORT_WORDS),
      };
    }

    const text = res.value;
    const { winnerIndex, report } = parseJudgeResponse(text);
    let selectedProvider: AIProviderName | 'unknown' = 'unknown';

    if (winnerIndex !== null && winnerIndex >= 0 && winnerIndex < candidates.length) {
      selectedProvider = candidates[winnerIndex].provider;
    }

    const fallbackReport = buildFallbackJudgeReport(selectedProvider, candidates, strings);

    return {
      judge,
      selectedProvider,
      reasoning: text,
      report: normalizeJudgeReport(report, fallbackReport),
    };
  });
}

export const PROVIDERS = ['claude', 'gemini', 'openai'] as const;

export const getProviderLabels = (strings: Strings): Record<string, string> => ({
  claude: strings.seeLog.castle.claude,
  gemini: strings.seeLog.castle.gemini,
  openai: strings.seeLog.castle.gpt,
});

export const PROVIDER_COLORS: Record<string, string> = {
  claude: '#D97706',
  gemini: '#4285F4',
  openai: '#10A37F',
};

function NutrientComparisonBar({
  nutrientKey,
  predictionsByProvider,
  maxValue,
  visibleProviders,
}: {
  nutrientKey: NutrientKey;
  predictionsByProvider: Map<string, FoodLogPred>;
  maxValue: number;
  visibleProviders: readonly string[];
}) {
  const c = useColors();
  const meta = NUTRIENT_META[nutrientKey];
  if (!meta) return null;

  const values = visibleProviders.map(provider => {
    const pred = predictionsByProvider.get(provider);
    return pred?.nutrition?.[nutrientKey] ?? 0;
  });

  const sortedIndices = values
    .map((v, i) => ({ v, i }))
    .sort((a, b) => b.v - a.v)
    .map(x => x.i);

  return (
    <View style={styles.compBarRow}>
      <Text style={[styles.compBarLabel, { color: c.text }]} numberOfLines={1}>
        {meta.label}
      </Text>
      <View style={styles.compBarContainer}>
        <View style={[styles.compBarBg, { backgroundColor: c.border }]}>
          {sortedIndices.map(idx => {
            const provider = visibleProviders[idx];
            const val = values[idx];
            if (val === 0) return null;
            const pct = Math.min((val / maxValue) * 100, 100);
            return (
              <View
                key={provider}
                style={[
                  styles.compBarFill,
                  {
                    width: `${pct}%`,
                    backgroundColor: PROVIDER_COLORS[provider],
                  },
                ]}
              />
            );
          })}
        </View>
        <View style={styles.compBarValues}>
          {visibleProviders.map(provider => {
            const pred = predictionsByProvider.get(provider);
            const val = pred?.nutrition?.[nutrientKey];
            if (val == null || val === 0) return null;
            const display = val >= 10 ? Math.round(val) : Math.round(val * 10) / 10;
            return (
              <Text
                key={provider}
                style={[styles.compBarValue, { color: PROVIDER_COLORS[provider] }]}
              >
                {display}
              </Text>
            );
          })}
        </View>
      </View>
    </View>
  );
}

export function NutritionComparisonChart({
  predictions,
  visibleProviders,
  providerLabels,
}: {
  predictions: FoodLogPred[];
  visibleProviders: readonly string[];
  providerLabels: Record<string, string>;
}) {
  const c = useColors();
  const strings = useStrings();
  const predictionsByProvider = useMemo(
    () => new Map(predictions.map(p => [p.aiModel, p])),
    [predictions]
  );

  const { nutrientKeys, maxValues } = useMemo(() => {
    const allKeys = new Set<NutrientKey>();
    const maxVals: Record<string, number> = {};

    for (const pred of predictions) {
      if (!pred.nutrition) continue;
      for (const [key, val] of Object.entries(pred.nutrition)) {
        if (val != null && val > 0) {
          allKeys.add(key as NutrientKey);
          maxVals[key] = Math.max(maxVals[key] ?? 0, val);
        }
      }
    }

    const sorted = Array.from(allKeys).sort((a, b) => {
      const metaA = NUTRIENT_META[a];
      const metaB = NUTRIENT_META[b];
      return (metaA?.importance ?? 999) - (metaB?.importance ?? 999);
    });

    return { nutrientKeys: sorted, maxValues: maxVals };
  }, [predictions]);

  if (nutrientKeys.length === 0) {
    return (
      <Text style={[styles.noDataText, { color: c.tertiaryText }]}>
        {strings.seeLog.castle.noNutritionData}
      </Text>
    );
  }

  return (
    <View style={styles.compChart}>
      <View style={styles.compLegend}>
        {visibleProviders.map(provider => (
          <View key={provider} style={styles.legendItem}>
            <View style={[styles.legendDot, { backgroundColor: PROVIDER_COLORS[provider] }]} />
            <Text style={[styles.legendText, { color: c.secondaryText }]}>
              {providerLabels[provider]}
            </Text>
          </View>
        ))}
      </View>
      {nutrientKeys.map(key => (
        <NutrientComparisonBar
          key={key}
          nutrientKey={key}
          predictionsByProvider={predictionsByProvider}
          maxValue={maxValues[key] * 1.1}
          visibleProviders={visibleProviders}
        />
      ))}
    </View>
  );
}

export function MultiProviderIngredients({
  predictions,
  visibleProviders,
}: {
  predictions: FoodLogPred[];
  visibleProviders: readonly string[];
}) {
  const c = useColors();
  return (
    <View style={styles.ingredientComp}>
      {visibleProviders.map(provider => {
        const pred = predictions.find(p => p.aiModel === provider);
        return (
          <View key={provider} style={styles.ingredientCompRow}>
            <View style={[styles.providerDot, { backgroundColor: PROVIDER_COLORS[provider] }]} />
            <View style={styles.ingredientCompContent}>
              <Text style={[styles.ingredientCompName, { color: c.text }]}>
                {pred?.predictedFoodName ?? '—'}
              </Text>
              {pred?.ingredients && pred.ingredients.length > 0 && (
                <Text
                  style={[styles.ingredientCompList, { color: c.secondaryText }]}
                  numberOfLines={2}
                >
                  {pred.ingredients.join(', ')}
                </Text>
              )}
            </View>
          </View>
        );
      })}
    </View>
  );
}

export function JudgeResultsView({
  results,
  providerLabels,
}: {
  results: JudgeResult[];
  providerLabels: Record<string, string>;
}) {
  const c = useColors();
  const strings = useStrings();
  return (
    <View style={styles.judgeResults}>
      {results.map((res, i) => (
        <View key={i} style={styles.judgeRow}>
          <View style={styles.judgeHeader}>
            <Text style={[styles.judgeName, { color: PROVIDER_COLORS[res.judge] }]}>
              {providerLabels[res.judge]}
            </Text>
            <Text style={[styles.judgeAction, { color: c.secondaryText }]}>
              {strings.seeLog.castle.votedFor}
            </Text>
            <Text
              style={[
                styles.judgeWinner,
                {
                  color:
                    res.selectedProvider !== 'unknown'
                      ? PROVIDER_COLORS[res.selectedProvider]
                      : c.text,
                },
              ]}
            >
              {res.selectedProvider !== 'unknown'
                ? providerLabels[res.selectedProvider]
                : strings.common.unknown}
            </Text>
          </View>
          <Text style={[styles.judgeReportLabel, { color: c.tertiaryText }]}>
            {strings.seeLog.castle.report}
          </Text>
          <Text style={[styles.judgeReason, { color: c.secondaryText }]}>{res.report}</Text>
        </View>
      ))}
    </View>
  );
}

function useJudge(
  photoUri: string | undefined,
  predictions: FoodLogPred[] | null,
  strings: Strings,
  showAlert: (title: string, message?: string) => void
) {
  const [judgeResults, setJudgeResults] = useState<JudgeResult[] | null>(null);
  const [judging, setJudging] = useState(false);

  const handleJudge = async () => {
    if (!photoUri || !predictions) return;
    setJudging(true);
    try {
      const base64 = await resizeAndEncodeImage(photoUri);
      const candidates = predictions.map(p => {
        const nutrition: Record<string, number> = {};
        if (p.nutrition) {
          for (const [k, v] of Object.entries(p.nutrition)) {
            if (v != null) nutrition[k] = v;
          }
        }
        return {
          provider: p.aiModel as 'claude' | 'gemini' | 'openai',
          result: {
            foodName: p.predictedFoodName ?? strings.common.unknown,
            ingredients: p.ingredients ?? [],
            nutrition,
          },
        };
      });
      const results = await judgePredictions(base64, candidates, strings);
      setJudgeResults(results);
    } catch {
      showAlert(strings.common.error, strings.seeLog.castle.failedToJudgePredictions);
    } finally {
      setJudging(false);
    }
  };

  return { judgeResults, judging, handleJudge };
}

export function CastleDebugSections({
  predictions,
  photoUri,
}: {
  predictions: FoodLogPred[];
  photoUri: string | undefined;
}) {
  const c = useColors();
  const strings = useStrings();
  const { showAlert } = useAlert();
  const providerLabels = getProviderLabels(strings);
  const { judgeResults, judging, handleJudge } = useJudge(
    photoUri,
    predictions,
    strings,
    showAlert
  );

  return (
    <>
      <View style={[styles.section, { backgroundColor: c.surface, borderColor: c.border }]}>
        <View style={styles.sectionHeader}>
          <Text style={[styles.sectionTitle, { color: c.text }]}>
            {strings.seeLog.castle.whatEachAiDetected}
          </Text>
          <View style={styles.castlePill}>
            <Text style={styles.castlePillText}>{strings.seeLog.castle.castle}</Text>
          </View>
        </View>
        <MultiProviderIngredients predictions={predictions} visibleProviders={PROVIDERS} />
      </View>

      <View style={[styles.section, { backgroundColor: c.surface, borderColor: c.border }]}>
        <View style={styles.sectionHeader}>
          <Text style={[styles.sectionTitle, { color: c.text }]}>
            {strings.seeLog.castle.nutritionComparison}
          </Text>
          <View style={styles.castlePill}>
            <Text style={styles.castlePillText}>{strings.seeLog.castle.castle}</Text>
          </View>
        </View>
        <NutritionComparisonChart
          predictions={predictions}
          visibleProviders={PROVIDERS}
          providerLabels={providerLabels}
        />
      </View>

      {!judgeResults && (
        <View
          style={[styles.judgeBtn, { backgroundColor: c.accent, opacity: judging ? 0.7 : 1 }]}
          onTouchEnd={judging ? undefined : handleJudge}
        >
          <Text style={[styles.judgeBtnText, { color: c.buttonText }]}>
            {judging ? strings.seeLog.castle.judging : strings.seeLog.castle.judgeResults}
          </Text>
        </View>
      )}

      {judgeResults && (
        <View style={[styles.section, { backgroundColor: c.surface, borderColor: c.border }]}>
          <Text style={[styles.sectionTitle, { color: c.text, marginBottom: 12 }]}>
            {strings.seeLog.castle.theVerdict}
          </Text>
          <JudgeResultsView results={judgeResults} providerLabels={providerLabels} />
        </View>
      )}
    </>
  );
}

const styles = StyleSheet.create({
  section: {
    marginTop: 20,
    borderRadius: 12,
    borderWidth: 1,
    padding: 16,
  },
  sectionHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 12,
  },
  sectionTitle: { fontSize: 16, fontWeight: '600' },
  castlePill: {
    backgroundColor: '#000',
    paddingHorizontal: 10,
    paddingVertical: 3,
    borderRadius: 10,
  },
  castlePillText: { color: '#fff', fontSize: 11, fontWeight: '600' },
  judgeBtn: {
    marginTop: 20,
    paddingVertical: 14,
    borderRadius: 12,
    alignItems: 'center',
  },
  judgeBtnText: {
    fontSize: 16,
    fontWeight: '600',
  },
  compChart: { gap: 10 },
  compLegend: { flexDirection: 'row', gap: 16, marginBottom: 8 },
  legendItem: { flexDirection: 'row', alignItems: 'center', gap: 4 },
  legendDot: { width: 8, height: 8, borderRadius: 4 },
  legendText: { fontSize: 11 },
  compBarRow: { flexDirection: 'row', alignItems: 'center', gap: 8 },
  compBarLabel: { width: 70, fontSize: 11, fontWeight: '500' },
  compBarContainer: { flex: 1, flexDirection: 'row', alignItems: 'center', gap: 6 },
  compBarBg: { flex: 1, height: 16, borderRadius: 4, overflow: 'hidden', position: 'relative' },
  compBarFill: { position: 'absolute', left: 0, top: 0, height: '100%', borderRadius: 4 },
  compBarValues: { flexDirection: 'row', gap: 4, minWidth: 80 },
  compBarValue: { fontSize: 10, fontWeight: '600' },
  noDataText: { fontSize: 13, textAlign: 'center', paddingVertical: 12 },
  ingredientComp: { gap: 12 },
  ingredientCompRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 10 },
  providerDot: { width: 10, height: 10, borderRadius: 5, marginTop: 5 },
  ingredientCompContent: { flex: 1 },
  ingredientCompName: { fontSize: 15, fontWeight: '500' },
  ingredientCompList: { fontSize: 12, marginTop: 2 },
  judgeResults: { gap: 16 },
  judgeRow: { gap: 4 },
  judgeHeader: { flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' },
  judgeName: { fontWeight: '700', fontSize: 15 },
  judgeAction: { fontSize: 14 },
  judgeWinner: { fontWeight: '700', fontSize: 15 },
  judgeReportLabel: { fontSize: 11, fontWeight: '600', textTransform: 'uppercase' },
  judgeReason: { fontSize: 13, lineHeight: 18 },
});
