import { Audio, InterruptionModeAndroid, InterruptionModeIOS } from "expo-av";
import { router, useFocusEffect } from "expo-router";
import { useCallback, useEffect, useRef, useState } from "react";
import Svg, { Path } from "react-native-svg";
import {
  ActivityIndicator,
  Alert,
  KeyboardAvoidingView,
  Platform,
  Pressable,
  ScrollView,
  StyleSheet,
  Text,
  View,
} from "react-native";
import { loadRecorderSettings } from "@/lib/recorder-settings";
import { getStrings, type Locale, t } from "@/lib/translations";

function formatDuration(durationMs: number) {
  const totalSeconds = Math.floor(durationMs / 1000);
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;

  return `${minutes.toString().padStart(2, "0")}:${seconds
    .toString()
    .padStart(2, "0")}`;
}

function buildMimeType(uri: string) {
  const extension = uri.split(".").pop()?.split("?")[0]?.toLowerCase();

  switch (extension) {
    case "wav":
      return "audio/wav";
    case "caf":
      return "audio/x-caf";
    case "webm":
      return "audio/webm";
    case "mp3":
      return "audio/mpeg";
    default:
      return "audio/m4a";
  }
}

function buildFileExtension(uri: string) {
  return uri.split(".").pop()?.split("?")[0]?.toLowerCase() || "m4a";
}

export default function RecorderScreen() {
  const [backendUrl, setBackendUrl] = useState("");
  const [authToken, setAuthToken] = useState("");
  const [fieldName, setFieldName] = useState("file");
  const [locale, setLocale] = useState<Locale>("ca");
  const [strings, setStrings] = useState(() => getStrings("ca"));
  const [recording, setRecording] = useState<Audio.Recording | null>(null);
  const [recordingUri, setRecordingUri] = useState<string | null>(null);
  const [recordingMs, setRecordingMs] = useState(0);
  const [statusMessage, setStatusMessage] = useState("");
  const [responsePreview, setResponsePreview] = useState("");
  const [llmResponseText, setLlmResponseText] = useState("");
  const [transcriptionText, setTranscriptionText] = useState("");
  const [isUploading, setIsUploading] = useState(false);
  const [isHolding, setIsHolding] = useState(false);
  const [isPlaying, setIsPlaying] = useState(false);
  const recordingRef = useRef<Audio.Recording | null>(null);
  const soundRef = useRef<Audio.Sound | null>(null);

  const refreshSettings = useCallback(() => {
    let isMounted = true;

    async function loadStoredValues() {
      try {
        const settings = await loadRecorderSettings();

        if (!isMounted) {
          return;
        }

        setBackendUrl(settings.backendUrl);
        setAuthToken(settings.authToken);
        setFieldName(settings.fieldName);
        setLocale(settings.language);
        setStrings(getStrings(settings.language));
      } catch {
        if (isMounted) {
          setStatusMessage(strings.loadError);
        }
      }
    }

    void loadStoredValues();

    return () => {
      isMounted = false;
    };
  }, []);

  useFocusEffect(refreshSettings);

  useEffect(() => {
    if (!recording) {
      return;
    }

    const interval = setInterval(() => {
      void recording.getStatusAsync().then((status) => {
        if (typeof status.durationMillis === "number") {
          setRecordingMs(status.durationMillis ?? 0);
        }
      });
    }, 250);

    return () => {
      clearInterval(interval);
    };
  }, [recording]);

  useEffect(() => {
    return () => {
      if (recording) {
        void recording.stopAndUnloadAsync().catch(() => undefined);
      }
    };
  }, [recording]);

  useEffect(() => {
    return () => {
      void unloadSound();
    };
  }, []);

  async function unloadSound() {
    if (soundRef.current) {
      try {
        await soundRef.current.stopAsync();
        await soundRef.current.unloadAsync();
      } catch (err) {
        console.log("[TTS] Error unloading sound:", err);
      }
      soundRef.current = null;
    }
    setIsPlaying(false);
  }

  async function speakWithAudio(audioUrl: string, backendBase: string) {
    if (!audioUrl) return false;

    await unloadSound();

    try {
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: false,
        playsInSilentModeIOS: true,
        interruptionModeAndroid: InterruptionModeAndroid.DoNotMix,
        interruptionModeIOS: InterruptionModeIOS.DoNotMix,
        shouldDuckAndroid: true,
        staysActiveInBackground: false,
      });
    } catch (err) {
      console.log("[TTS] Audio mode error:", err);
    }

    try {
      const fullUrl = audioUrl.startsWith("http")
        ? audioUrl
        : `${backendBase.replace(/\/+$/, "")}/${audioUrl.replace(/^\/+/, "")}`;

      console.log("[TTS] Loading audio from:", fullUrl);
      setIsPlaying(true);
      setStatusMessage(strings.playing);

      const { sound } = await Audio.Sound.createAsync(
        { uri: fullUrl },
        { shouldPlay: true, volume: 1.0 },
        (status) => {
          if (status.isLoaded && status.didJustFinish) {
            console.log("[TTS] Audio playback finished");
            void unloadSound();
          }
        },
      );

      soundRef.current = sound;
      const status = await sound.getStatusAsync();
      const durationMs = status.isLoaded ? (status.durationMillis ?? 0) : 0;
      console.log("[TTS] Playing audio, duration:", durationMs, "ms");

      return true;
    } catch (err) {
      console.log("[TTS] Audio playback error:", err);
      setIsPlaying(false);
      return false;
    }
  }

  async function speakSequentially(texts: string[]) {
    if (texts.length === 0) return;

    const trimmedUrl = backendUrl.trim().replace(/\/+$/, "");
    for (let i = 0; i < texts.length; i++) {
      const text = texts[i];
      if (!text || !text.trim()) continue;

      try {
        setStatusMessage(strings.playing);
        console.log("[TTS] Generating TTS audio for text:", text.substring(0, 50));

        const localeLang = locale === "ca" ? "ca" : "en";
        const ttsParams = new URLSearchParams({
          text: text.trim(),
          language: localeLang,
        });

        if (authToken.trim()) {
          ttsParams.append("token", authToken.trim());
        }

        const ttsUrl = `${trimmedUrl}/tts?${ttsParams.toString()}`;
        const ttsResponse = await fetch(ttsUrl, { method: "POST" });

        if (!ttsResponse.ok) {
          const errText = await ttsResponse.text();
          console.log("[TTS] TTS endpoint error:", ttsResponse.status, errText);
          continue;
        }

        const ttsData = await ttsResponse.json();

        if (!ttsData.audioUrl) {
          console.log("[TTS] No audioUrl in response:", ttsData);
          continue;
        }

        const played = await speakWithAudio(ttsData.audioUrl, trimmedUrl);
        if (!played) {
          setStatusMessage(strings.uploadFailed);
        }

        if (i < texts.length - 1) {
          await new Promise((r) => setTimeout(r, 800));
        }
      } catch (err) {
        console.log("[TTS] speakSequentially error:", err);
      }
    }
  }

  async function speak(text: string) {
    const texts = [text].filter(Boolean);
    await speakSequentially(texts);
  }

  async function startRecording() {
    try {
      await unloadSound();
      setTranscriptionText("");
      setResponsePreview("");
      setLlmResponseText("");
      setRecordingUri(null);

      const permission = await Audio.requestPermissionsAsync();

      if (!permission.granted) {
        setStatusMessage(strings.micPermissionDenied);
        Alert.alert(
          strings.micAccessRequiredTitle,
          strings.micAccessRequiredMsg,
        );
        return;
      }

      await Audio.setAudioModeAsync({
        allowsRecordingIOS: true,
        interruptionModeAndroid: InterruptionModeAndroid.DoNotMix,
        interruptionModeIOS: InterruptionModeIOS.DoNotMix,
        playsInSilentModeIOS: true,
        shouldDuckAndroid: true,
        staysActiveInBackground: false,
      });

      const result = await Audio.Recording.createAsync(
        Audio.RecordingOptionsPresets.HIGH_QUALITY,
      );

      recordingRef.current = result.recording;
      setRecording(result.recording);
      setRecordingMs(0);
      setStatusMessage(strings.recording);
    } catch (error) {
      setStatusMessage(strings.couldNotStartRecording);
      Alert.alert(
        strings.recordingFailedTitle,
        error instanceof Error ? error.message : "",
      );
    }
  }

  async function stopRecordingAndUpload() {
    if (!recordingRef.current) {
      return;
    }

    console.log("[APP] stopRecordingAndUpload called");

    try {
      const activeRecording = recordingRef.current;
      const currentStatus = await activeRecording.getStatusAsync();
      const durationMillis = currentStatus.durationMillis ?? 0;

      await activeRecording.stopAndUnloadAsync();
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: false,
        playsInSilentModeIOS: true,
      });

      const uri = activeRecording.getURI();
      recordingRef.current = null;
      setRecording(null);
      setRecordingMs(durationMillis);

      if (!uri) {
        setStatusMessage(strings.readyToRecord);
        return;
      }

      setRecordingUri(uri);
      setStatusMessage(strings.finishedUpload);

      const trimmedUrl = backendUrl.trim().replace(/\/+$/, '');
      const uploadUrl = trimmedUrl.endsWith('/audio/upload')
        ? trimmedUrl
        : `${trimmedUrl}/audio/upload`;
      if (uploadUrl) {
        setIsUploading(true);
        try {
          const mimeType = buildMimeType(uri);
          const extension = buildFileExtension(uri);
          const formData = new FormData();

          formData.append(fieldName.trim() || "file", {
            name: `recording-${Date.now()}.${extension}`,
            type: mimeType,
            uri: uri,
          } as never);

          const headers: Record<string, string> = {};
          if (authToken.trim()) {
            headers.Authorization = `Bearer ${authToken.trim()}`;
          }

          const response = await fetch(uploadUrl, {
            method: "POST",
            headers,
            body: formData,
          });

          const responseText = await response.text();

          if (!response.ok) {
            throw new Error(`${response.status}. ${responseText}`);
          }

          try {
            const data = JSON.parse(responseText);
            setResponsePreview(responseText.slice(0, 400));

            const textsToSpeak: string[] = [];

            if (data.transcription) {
              setTranscriptionText(data.transcription);
            }

            if (data.llmResponse) {
              setLlmResponseText(data.llmResponse);
              textsToSpeak.push(data.llmResponse);
            }

            if (textsToSpeak.length > 0) {
              setStatusMessage(strings.voiceMessageSent + ". " + strings.playing);
              void speakSequentially(textsToSpeak);
            } else {
              setLlmResponseText("");
            }
          } catch (parseError) {
            console.log("[APP] JSON parse failed:", parseError, "Response was:", responseText.substring(0, 200));
            setResponsePreview(responseText.slice(0, 400));
            setTranscriptionText("");
            setLlmResponseText("");
          }

          setStatusMessage(strings.voiceMessageSent);
        } catch (error) {
          setStatusMessage(strings.uploadFailed);
          Alert.alert(
            strings.uploadFailed,
            error instanceof Error ? error.message : "",
          );
        } finally {
          setIsUploading(false);
        }
      } else {
        setStatusMessage(strings.noBackendUrl);
        setIsUploading(false);
      }
    } catch (error) {
      recordingRef.current = null;
      setRecording(null);
      setStatusMessage(strings.stopFailedTitle);
      Alert.alert(
        strings.stopFailedTitle,
        error instanceof Error ? error.message : "",
      );
    }
  }

  async function handlePressIn() {
    if (isUploading) return;
    setIsHolding(true);
    await startRecording();
  }

  async function handlePressOut() {
    if (!isHolding) return;
    setIsHolding(false);
    await stopRecordingAndUpload();
  }

  async function uploadRecording(uriOverride?: string) {
    const targetUri = uriOverride ?? recordingUri;

    if (!targetUri) {
      return;
    }

    const trimmedUrl = backendUrl.trim().replace(/\/+$/, '');
    const uploadUrl = trimmedUrl.endsWith('/audio/upload')
      ? trimmedUrl
      : `${trimmedUrl}/audio/upload`;

    if (!uploadUrl) {
      Alert.alert(strings.missingBackendUrlTitle, strings.missingBackendUrlMsg);
      return;
    }

    try {
      setIsUploading(true);
      setStatusMessage(strings.uploadingRecording);
      await unloadSound();
      setTranscriptionText("");
      setResponsePreview("");
      setLlmResponseText("");

      const mimeType = buildMimeType(targetUri);
      const extension = buildFileExtension(targetUri);
      const formData = new FormData();

      formData.append(fieldName.trim() || "file", {
        name: `recording-${Date.now()}.${extension}`,
        type: mimeType,
        uri: targetUri,
      } as never);

      const headers: Record<string, string> = {};

      if (authToken.trim()) {
        headers.Authorization = `Bearer ${authToken.trim()}`;
      }

      const response = await fetch(uploadUrl, {
        method: "POST",
        headers,
        body: formData,
      });

      const responseText = await response.text();

      if (!response.ok) {
        throw new Error(`${response.status}. ${responseText}`);
      }

      try {
        const data = JSON.parse(responseText);
        setResponsePreview(responseText.slice(0, 400));

        const textsToSpeak: string[] = [];

        if (data.transcription) {
          setTranscriptionText(data.transcription);
          textsToSpeak.push(data.transcription);
        }

        if (data.llmResponse) {
          setLlmResponseText(data.llmResponse);
          textsToSpeak.push(data.llmResponse);
        }

        if (textsToSpeak.length > 0) {
          setStatusMessage(strings.voiceMessageSent + ". " + strings.playing);
          void speakSequentially(textsToSpeak);
        } else {
          setLlmResponseText("");
        }
      } catch {
        setResponsePreview(responseText.slice(0, 400));
        setTranscriptionText("");
        setLlmResponseText("");
      }

      setStatusMessage(strings.uploadComplete);
    } catch (error) {
      setStatusMessage(strings.uploadFailed);
      Alert.alert(
        strings.uploadFailed,
        error instanceof Error ? error.message : "",
      );
    } finally {
      setIsUploading(false);
    }
  }

  function handleSpeak() {
    const texts = [transcriptionText, llmResponseText].filter(Boolean);
    void speakSequentially(texts);
  }

  const releaseLabel = t("releaseToStop", locale);
  const holdLabel = t("holdToRecord", locale);
  const openSettingsLabel = t("openSettingsHint", locale);
  const appTitleLabel = t("appTitle", locale);
  const recorderTitleLabel = t("recorderTitle", locale);
  const serverResponseLabel = t("serverResponse", locale);

  return (
    <View style={styles.safeArea}>
      <KeyboardAvoidingView
        style={styles.keyboardAvoidingView}
        behavior={Platform.OS === "ios" ? "padding" : undefined}
      >
        <ScrollView
          style={styles.scrollView}
          contentContainerStyle={styles.content}
          keyboardShouldPersistTaps="handled"
        >
          <View style={styles.hero}>
            <View style={styles.heroTopRow}>
              <View style={styles.heroBadge}>
                <Text style={styles.heroBadgeText}>{appTitleLabel}</Text>
              </View>
              <Pressable
                onPress={() => router.push("/settings")}
                hitSlop={10}
                style={styles.settingsCog}
              >
                <Svg width="20" height="20" viewBox="0 0 24 24" fill="none">
                  <Path
                    d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"
                    fill="#d3deea"
                  />
                  <Path
                    d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1.08-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l-.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1.08 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1.08 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9c.26.604.852.997 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1.08Z"
                    stroke="#d3deea"
                    strokeWidth="1.5"
                  />
                </Svg>
              </Pressable>
            </View>
          </View>

          <View style={styles.panel}>
            <Text style={[styles.meterValueCentered, isHolding && { color: "#d04f2d" }]}>
              {formatDuration(recordingMs)}
            </Text>

            <Pressable
              disabled={isUploading}
              onPressIn={handlePressIn}
              onPressOut={handlePressOut}
              style={[
                styles.micButton,
                isHolding ? styles.holdingButton : styles.idleButton,
                isUploading && styles.buttonDisabled,
              ]}
            >
              {isUploading ? (
                <ActivityIndicator color="#fff6f3" size="large" />
              ) : (
                <Svg width="64" height="64" viewBox="0 0 24 24" fill="none">
                  <Path
                    d="M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3z"
                    fill="#fff6f3"
                    stroke="#fff6f3"
                    strokeWidth="1"
                  />
                  <Path
                    d="M19 10v1a7 7 0 0 1-14 0v-1"
                    stroke="#fff6f3"
                    strokeWidth="2"
                    strokeLinecap="round"
                  />
                  <Path
                    d="M12 18v3"
                    stroke="#fff6f3"
                    strokeWidth="2"
                    strokeLinecap="round"
                  />
                </Svg>
              )}
            </Pressable>

            <Text style={styles.statusText}>{statusMessage || strings.readyToRecord}</Text>
            <Text style={styles.helperText}>
              {isHolding
                ? releaseLabel
                : backendUrl.trim()
                  ? holdLabel
                  : openSettingsLabel}
            </Text>

            {transcriptionText ? (
              <View style={styles.transcriptionBox}>
                <Text style={styles.transcriptionLabel}>{strings.yourMessage}</Text>
                <Text style={styles.transcriptionText}>{transcriptionText}</Text>
              </View>
            ) : null}

            {llmResponseText ? (
              <View style={styles.llmResponseBox}>
                <View style={styles.llmResponseHeader}>
                  <Text style={styles.llmResponseLabel}>{strings.aiReply}</Text>
                  <Pressable onPress={handleSpeak} style={styles.speakButton}>
                    <Svg width="20" height="20" viewBox="0 0 24 24" fill="none">
                      <Path d="M11 5L6 9H2v6h4l5 4V5z" fill="#13304a" />
                      <Path d="M15.5 8.5a5.5 5.5 0 0 1 0 7" stroke="#13304a" strokeWidth="2" strokeLinecap="round" />
                      <Path d="M18.5 5.5a9 9 0 0 1 0 13" stroke="#13304a" strokeWidth="2" strokeLinecap="round" />
                    </Svg>
                  </Pressable>
                </View>
                <Text style={styles.llmResponseText}>{llmResponseText}</Text>
              </View>
            ) : null}

            {responsePreview && !transcriptionText && !llmResponseText ? (
              <View style={styles.responseBox}>
                <Text style={styles.responseLabel}>{serverResponseLabel}</Text>
                <Text style={styles.responseText}>{responsePreview}</Text>
              </View>
            ) : null}
          </View>
        </ScrollView>
      </KeyboardAvoidingView>
    </View>
  );
}

const styles = StyleSheet.create({
  safeArea: {
    flex: 1,
    backgroundColor: "#f4efe4",
  },
  keyboardAvoidingView: {
    flex: 1,
  },
  scrollView: {
    flex: 1,
  },
  content: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    paddingVertical: 32,
    paddingHorizontal: 20,
    gap: 18,
  },
  hero: {
    backgroundColor: "transparent",
    paddingHorizontal: 22,
    paddingTop: 40,
  },
  heroTopRow: {
    alignItems: "center",
    flexDirection: "row",
    justifyContent: "space-between",
  },
  heroBadge: {
    backgroundColor: "#f2b15d",
    borderRadius: 999,
    paddingHorizontal: 12,
    paddingVertical: 6,
  },
  heroBadgeText: {
    color: "#13304a",
    fontSize: 12,
    fontWeight: "700",
    letterSpacing: 0.5,
    textTransform: "uppercase",
  },
  settingsCog: {
    alignItems: "center",
    justifyContent: "center",
    width: 40,
    height: 40,
    borderRadius: 999,
    backgroundColor: "#13304a",
    marginLeft: 12,
  },
  panel: {
    backgroundColor: "#fffaf1",
    borderColor: "#dccfb9",
    borderRadius: 24,
    borderWidth: 1,
    gap: 12,
    padding: 18,
    alignSelf: "center",
    maxWidth: 340,
  },
  meterValueCentered: {
    color: "#d04f2d",
    fontSize: 40,
    fontWeight: "800",
    textAlign: "center",
  },
  micButton: {
    alignItems: "center",
    borderRadius: 999,
    height: 164,
    justifyContent: "center",
    marginVertical: 6,
    width: 164,
    alignSelf: "center",
  },
  idleButton: {
    backgroundColor: "#13304a",
  },
  holdingButton: {
    backgroundColor: "#d04f2d",
    transform: [{ scale: 1.08 }],
  },
  micButtonText: {
    color: "#fff6f3",
    fontSize: 20,
    fontWeight: "800",
  },
  recordingLabel: {
    fontSize: 18,
  },
  buttonDisabled: {
    opacity: 0.45,
  },
  statusText: {
    color: "#1f2d3d",
    fontSize: 15,
    lineHeight: 21,
    textAlign: "center",
  },
  helperText: {
    color: "#665f54",
    fontSize: 13,
    lineHeight: 18,
    textAlign: "center",
  },
  responseBox: {
    backgroundColor: "#f7f0e0",
    borderRadius: 16,
    gap: 6,
    marginTop: 4,
    padding: 14,
  },
  responseLabel: {
    color: "#13304a",
    fontSize: 13,
    fontWeight: "700",
    textTransform: "uppercase",
    textAlign: "center",
  },
  responseText: {
    color: "#36475a",
    fontSize: 14,
    lineHeight: 20,
  },
  llmResponseBox: {
    backgroundColor: "#e8f4e8",
    borderRadius: 16,
    gap: 6,
    marginTop: 4,
    padding: 14,
    borderWidth: 1,
    borderColor: "#b8d9b8",
  },
  llmResponseHeader: {
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
  },
  llmResponseLabel: {
    color: "#2a6a2a",
    fontSize: 13,
    fontWeight: "700",
    textTransform: "uppercase",
    textAlign: "center",
  },
  llmResponseText: {
    color: "#2d4a2d",
    fontSize: 16,
    lineHeight: 24,
  },
  speakButton: {
    backgroundColor: "#f7f0e0",
    borderRadius: 20,
    padding: 6,
    borderWidth: 1,
    borderColor: "#dccfb9",
  },
  transcriptionBox: {
    backgroundColor: "#e8ecf4",
    borderRadius: 16,
    gap: 6,
    marginTop: 4,
    padding: 14,
    borderWidth: 1,
    borderColor: "#b8c9d9",
  },
  transcriptionLabel: {
    color: "#1a4a6a",
    fontSize: 13,
    fontWeight: "700",
    textTransform: "uppercase",
    textAlign: "center",
  },
  transcriptionText: {
    color: "#1f3a52",
    fontSize: 16,
    lineHeight: 24,
  },
});
