import { useState, useEffect, useRef } from "react";
import { invoke } from "@tauri-apps/api/core";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import "./App.css";
import { TranscriptDisplay } from "./components/TranscriptDisplay";
import { SummaryDisplay } from "./components/SummaryDisplay";
import { BackendLogs } from "./components/BackendLogs";
import { ProgressBar } from "./components/ProgressBar";

interface TranscriptSegment {
  start: number;
  end: number;
  text: string;
  speaker: string;
}

interface StoredTranscriptSegment {
  start: number;
  end: number;
  text: string;
  speaker: string;
}

interface StoredRecording {
  id: string;
  timestamp: string; // ISO 8601 string
  duration: number;
  transcript_segments: StoredTranscriptSegment[];
  summary: string | null;
  title: string | null;
}

interface Recording {
  id: string;
  timestamp: Date;
  duration: number;
  transcriptSegments: TranscriptSegment[];
  summary: string | null;
  title: string | null;
  isGeneratingSummary: boolean;
  summaryProgress?: number;
}

type AppState = "checking" | "downloading_models" | "initializing" | "ready" | "recording" | "transcribing" | "error";

function App() {
  const [recordings, setRecordings] = useState<Recording[]>([]);
  const [activeRecording, setActiveRecording] = useState<Recording | null>(null);
  const [selectedRecordingId, setSelectedRecordingId] = useState<string | null>(null);
  const [appState, setAppState] = useState<AppState>("checking");
  const [statusMessage, setStatusMessage] = useState("Checking setup...");
  const [showLogs, setShowLogs] = useState(false);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [recordingDuration, setRecordingDuration] = useState(0);
  const [transcriptionProgress, setTranscriptionProgress] = useState(0);
  const initStarted = useRef(false);
  const recordingTimer = useRef<number | null>(null);
  const transcriptionTimer = useRef<number | null>(null);
  const audioOffset = useRef(0);
  const totalProcessedSamples = useRef(0);
  const transcriptionProgressUnlisten = useRef<UnlistenFn | null>(null);
  const summaryProgressUnlisten = useRef<UnlistenFn | null>(null);

  useEffect(() => {
    if (initStarted.current) return;
    initStarted.current = true;

    initializeApp();
  }, []);

  // Helper function to convert stored recording to frontend format
  const storedToFrontend = (stored: StoredRecording): Recording => ({
    id: stored.id,
    timestamp: new Date(stored.timestamp),
    duration: stored.duration,
    transcriptSegments: stored.transcript_segments,
    summary: stored.summary,
    title: stored.title,
    isGeneratingSummary: false,
    summaryProgress: undefined,
  });

  // Helper function to convert frontend recording to stored format
  const frontendToStored = (recording: Recording): StoredRecording => ({
    id: recording.id,
    timestamp: recording.timestamp.toISOString(),
    duration: recording.duration,
    transcript_segments: recording.transcriptSegments,
    summary: recording.summary,
    title: recording.title,
  });

  // Cleanup timers and listeners on unmount
  useEffect(() => {
    return () => {
      if (recordingTimer.current) {
        clearInterval(recordingTimer.current);
      }
      if (transcriptionTimer.current) {
        clearInterval(transcriptionTimer.current);
      }
      if (transcriptionProgressUnlisten.current) {
        transcriptionProgressUnlisten.current();
      }
      if (summaryProgressUnlisten.current) {
        summaryProgressUnlisten.current();
      }
    };
  }, []);

  // Set up event listeners for progress updates
  useEffect(() => {
    const setupListeners = async () => {
      // Listen for transcription progress
      transcriptionProgressUnlisten.current = await listen<number>('transcription-progress', (event) => {
        setTranscriptionProgress(event.payload * 100); // Convert 0-1 to 0-100
      });

      // Listen for summary progress
      summaryProgressUnlisten.current = await listen<number>('summary-progress', (event) => {
        const recordingId = selectedRecordingId || recordings[0]?.id;
        if (recordingId) {
          setRecordings(prev => prev.map(r =>
            r.id === recordingId ? { ...r, summaryProgress: event.payload * 100 } : r
          ));
        }
      });
    };

    setupListeners();
  }, [selectedRecordingId, recordings]);

  const initializeApp = async () => {
    try {
      setAppState("checking");
      setStatusMessage("Checking if models are present...");

      const modelsPresent = await invoke<boolean>("check_models");

      if (!modelsPresent) {
        setAppState("downloading_models");
        setStatusMessage("Downloading AI models (~2GB)...");
        setShowLogs(true);

        await invoke("download_models");

        // Check again after download
        const modelsVerified = await invoke<boolean>("check_models");
        if (!modelsVerified) {
          throw new Error("Download completed but model file not found. This might be a path or permissions issue.");
        }
      }

      // Initialize ML models
      setAppState("initializing");
      setStatusMessage("Loading AI models (this may take a moment)...");
      setShowLogs(true);

      await invoke("initialize_models");

      // Check if models are ready
      const ready = await invoke<boolean>("check_ready");
      if (!ready) {
        throw new Error("Models failed to initialize. Check logs for details.");
      }

      setAppState("ready");
      setStatusMessage("");
      setShowLogs(false);

      // Load saved recordings
      try {
        const savedRecordings = await invoke<StoredRecording[]>("load_recordings");
        const loadedRecordings = savedRecordings.map(storedToFrontend);
        setRecordings(loadedRecordings);
        console.log(`Loaded ${loadedRecordings.length} transcripts from storage`);
      } catch (loadError) {
        console.error("Failed to load transcripts:", loadError);
        // Don't fail app init if we can't load transcripts
      }
    } catch (error) {
      console.error("Initialization failed:", error);
      setAppState("error");
      setErrorMessage(String(error));
      setShowLogs(true);
    }
  };

  const retrySetup = () => {
    setErrorMessage(null);
    initStarted.current = false;
    initializeApp();
  };

  const processAudioChunk = async () => {
    try {
      // Get the next chunk of audio
      const [audioChunk, newOffset] = await invoke<[number[], number]>("get_audio_chunk", {
        lastOffset: audioOffset.current
      });

      // If we have enough audio (at least 5 seconds worth at 16kHz)
      if (audioChunk.length >= 5 * 16000) {
        // Transcribe the chunk
        const chunkStartTime = totalProcessedSamples.current / 16000;
        const newSegments = await invoke<TranscriptSegment[]>("transcribe_chunk", {
          audioData: audioChunk,
          chunkStartTime: chunkStartTime
        });

        if (newSegments.length > 0) {
          // Calculate timestamps based on total processed samples
          const baseTime = totalProcessedSamples.current / 16000;
          const adjustedSegments = newSegments.map(seg => ({
            ...seg,
            start: seg.start + baseTime,
            end: seg.end + baseTime,
          }));

          setActiveRecording(prev => prev ? {
            ...prev,
            transcriptSegments: [...prev.transcriptSegments, ...adjustedSegments]
          } : null);
        }

        // Track total processed samples
        totalProcessedSamples.current += audioChunk.length;

        // Update the offset for next time
        audioOffset.current = newOffset;
      }
    } catch (error) {
      console.error("Failed to process audio chunk:", error);
    }
  };

  const startRecording = async () => {
    try {
      setAppState("recording");
      setRecordingDuration(0);
      audioOffset.current = 0;
      totalProcessedSamples.current = 0;

      // Create a new active recording
      const newRecording: Recording = {
        id: Date.now().toString(),
        timestamp: new Date(),
        duration: 0,
        transcriptSegments: [],
        summary: null,
        title: null,
        isGeneratingSummary: false,
      };
      setActiveRecording(newRecording);

      await invoke("start_recording");

      // Start timer to show recording duration
      recordingTimer.current = window.setInterval(() => {
        setRecordingDuration(d => d + 1);
      }, 1000);

      // Start real-time transcription timer (every 5 seconds)
      transcriptionTimer.current = window.setInterval(() => {
        processAudioChunk();
      }, 5000);
    } catch (error) {
      console.error("Failed to start recording:", error);
      setAppState("ready");
      setErrorMessage(String(error));
    }
  };

  const stopRecording = async () => {
    try {
      // Stop the timers first
      if (recordingTimer.current) {
        clearInterval(recordingTimer.current);
        recordingTimer.current = null;
      }
      if (transcriptionTimer.current) {
        clearInterval(transcriptionTimer.current);
        transcriptionTimer.current = null;
      }

      setAppState("transcribing");
      setStatusMessage("Processing final audio...");
      setTranscriptionProgress(0);

      // First, process any audio that hasn't been processed yet
      try {
        const finalChunk = await invoke<number[]>("get_remaining_audio", {
          lastOffset: audioOffset.current
        });

        if (finalChunk.length > 0) {
          console.log(`Processing final chunk of ${finalChunk.length} samples`);
          const chunkStartTime = totalProcessedSamples.current / 16000;
          // The progress will be updated via events from the backend
          const finalSegments = await invoke<TranscriptSegment[]>("transcribe_chunk", {
            audioData: finalChunk,
            chunkStartTime: chunkStartTime
          });

          if (finalSegments.length > 0) {
            const baseTime = totalProcessedSamples.current / 16000;
            const adjustedSegments = finalSegments.map(seg => ({
              ...seg,
              start: seg.start + baseTime,
              end: seg.end + baseTime,
            }));

            setActiveRecording(prev => prev ? {
            ...prev,
            transcriptSegments: [...prev.transcriptSegments, ...adjustedSegments]
          } : null);
          }
        }
      } catch (chunkError) {
        console.error("Error processing final chunk:", chunkError);
        // Continue with stopping even if final chunk fails
      }

      // Now stop the recording
      await invoke<string>("stop_recording");

      // Save the recording to history
      if (activeRecording) {
        const finalRecording = {
          ...activeRecording,
          duration: recordingDuration
        };
        setRecordings(prev => [finalRecording, ...prev]);
        setSelectedRecordingId(finalRecording.id);
        setActiveRecording(null);

        // Save to persistent storage
        try {
          await invoke("save_recording", {
            recording: frontendToStored(finalRecording)
          });
          console.log("Transcript saved to storage");
        } catch (saveError) {
          console.error("Failed to save transcript:", saveError);
          // Don't fail the whole operation if storage fails
        }
      }

      // Brief delay to show completion
      await new Promise(resolve => setTimeout(resolve, 500));

      setAppState("ready");
      setStatusMessage("");
      setTranscriptionProgress(0);
    } catch (error) {
      console.error("Failed to stop recording:", error);
      setAppState("ready");
      setStatusMessage("");
      setErrorMessage(String(error));
    }
  };

  const generateSummary = async (recordingId: string) => {
    const recording = recordings.find(r => r.id === recordingId);
    if (!recording || recording.transcriptSegments.length === 0) return;

    // Update the recording to show it's generating
    setRecordings(prev => prev.map(r =>
      r.id === recordingId ? { ...r, isGeneratingSummary: true, summaryProgress: 0 } : r
    ));

    const fullTranscript = recording.transcriptSegments
      .map((seg) => `${seg.speaker}: ${seg.text}`)
      .join("\n");

    try {
      // Progress will be updated via events from the backend
      const [summaryResult, titleResult] = await invoke<[string, string | null]>("summarize", { transcript: fullTranscript });

      // Update the recording with the summary and title
      const updatedRecording = recordings.find(r => r.id === recordingId);
      if (updatedRecording) {
        const recordingWithSummary = {
          ...updatedRecording,
          summary: summaryResult,
          title: titleResult,
          isGeneratingSummary: false,
          summaryProgress: 100
        };

        setRecordings(prev => prev.map(r =>
          r.id === recordingId ? recordingWithSummary : r
        ));

        // Update in persistent storage
        try {
          await invoke("update_recording", {
            recording: frontendToStored(recordingWithSummary)
          });
          console.log("Transcript updated with summary");
        } catch (updateError) {
          console.error("Failed to update transcript:", updateError);
        }

        // Clear progress after a brief delay
        setTimeout(() => {
          setRecordings(prev => prev.map(r =>
            r.id === recordingId ? { ...r, summaryProgress: undefined } : r
          ));
        }, 1000);
      }
    } catch (error) {
      console.error("Failed to generate summary:", error);
      setErrorMessage(String(error));

      // Reset generating state on error
      setRecordings(prev => prev.map(r =>
        r.id === recordingId ? { ...r, isGeneratingSummary: false, summaryProgress: undefined } : r
      ));
    }
  };

  const copyToClipboard = async (text: string) => {
    try {
      await navigator.clipboard.writeText(text);
      // You could add a toast notification here
    } catch (error) {
      console.error("Failed to copy to clipboard:", error);
    }
  };

  const deleteRecording = async (recordingId: string) => {
    // Confirm deletion
    if (!confirm("Are you sure you want to delete this transcript? This action cannot be undone.")) {
      return;
    }

    try {
      // Delete from backend storage
      await invoke("delete_recording", { recordingId });

      // Remove from state
      setRecordings(prev => prev.filter(r => r.id !== recordingId));

      // Clear selection if we deleted the selected recording
      if (selectedRecordingId === recordingId) {
        setSelectedRecordingId(null);
      }

      console.log(`Transcript ${recordingId} deleted`);
    } catch (error) {
      console.error("Failed to delete transcript:", error);
      alert(`Failed to delete transcript: ${error}`);
    }
  };

  const downloadTranscript = (recordingId: string) => {
    const recording = recordings.find(r => r.id === recordingId);
    if (!recording) return;

    const content = recording.transcriptSegments
      .map((seg) => `[${formatTime(seg.start)}] ${seg.speaker}: ${seg.text}`)
      .join("\n");

    const blob = new Blob([content], { type: "text/plain" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `meeting-transcript-${recording.timestamp.toISOString().split("T")[0]}.txt`;
    a.click();
    URL.revokeObjectURL(url);
  };

  const downloadSummary = (recordingId: string) => {
    const recording = recordings.find(r => r.id === recordingId);
    if (!recording || !recording.summary) return;

    const blob = new Blob([recording.summary], { type: "text/plain" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `meeting-summary-${recording.timestamp.toISOString().split("T")[0]}.txt`;
    a.click();
    URL.revokeObjectURL(url);
  };

  const formatTime = (seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, "0")}`;
  };

  const formatDuration = (seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${mins}:${secs.toString().padStart(2, "0")}`;
  };

  // Get the currently selected recording or active recording
  const displayedRecording = activeRecording ||
    (selectedRecordingId ? recordings.find(r => r.id === selectedRecordingId) : null);

  const renderSetupScreen = () => (
    <div className="setup-screen">
      <div className="setup-content">
        <h2>
          {appState === "checking" && "🔍 Checking Setup..."}
          {appState === "downloading_models" && "📥 Downloading AI Models"}
          {appState === "initializing" && "🚀 Loading AI Models"}
          {appState === "error" && "❌ Setup Failed"}
        </h2>

        <p className="setup-message">{statusMessage}</p>

        {appState === "downloading_models" && (
          <div className="setup-info">
            <p>Downloading the AI models:</p>
            <ul>
              <li>Llama 3.2 3B Instruct (~2GB) - for summarization</li>
              <li>Whisper Base (~142MB) - for transcription</li>
            </ul>
            <p className="setup-note">
              This is a one-time download. Models are stored locally for offline use.
            </p>
          </div>
        )}

        {appState === "initializing" && (
          <div className="setup-info">
            <p>Loading the AI models into memory:</p>
            <ul>
              <li>LLaMA - Meeting summarization</li>
              <li>Whisper - Speech recognition</li>
              <li>VAD - Speaker detection</li>
            </ul>
            <p className="setup-note">
              This is a pure Rust backend - no Python required!
            </p>
          </div>
        )}

        {appState === "error" && (
          <div className="error-content">
            <p className="error-message">{errorMessage}</p>
            <button className="primary-button" onClick={retrySetup}>
              🔄 Retry Setup
            </button>
          </div>
        )}

        {(appState === "checking" || appState === "initializing" || appState === "downloading_models") && (
          <div className="loading-spinner" />
        )}
      </div>

      <BackendLogs isVisible={showLogs} onToggle={() => setShowLogs(!showLogs)} />
    </div>
  );

  const renderRecordingControls = () => (
    <section className="controls-section">
      {appState === "ready" && (
        <button className="record-button" onClick={startRecording}>
          🎙️ Start Transcribing
        </button>
      )}

      {appState === "recording" && (
        <div className="recording-controls">
          <div className="recording-indicator">
            <span className="recording-dot" />
            Recording: {formatDuration(recordingDuration)}
            {activeRecording && activeRecording.transcriptSegments.length > 0 && (
              <span className="real-time-indicator"> (Real-time transcription active)</span>
            )}
          </div>
          <button className="stop-button" onClick={stopRecording}>
            ⏹️ Stop Transcribing
          </button>
        </div>
      )}

      {appState === "transcribing" && (
        <div className="transcribing-indicator">
          <ProgressBar
            progress={transcriptionProgress}
            label="Processing final audio..."
          />
        </div>
      )}

    </section>
  );

  const renderRecordingsList = () => (
    <aside className="transcripts-list">
      <h3>Transcript History</h3>
      {recordings.length === 0 ? (
        <p className="no-transcripts">No transcripts yet</p>
      ) : (
        <ul className="transcript-items">
          {recordings.map(recording => (
            <li
              key={recording.id}
              className={`transcript-item ${selectedRecordingId === recording.id ? 'selected' : ''}`}
            >
              <div
                className="transcript-content"
                onClick={() => setSelectedRecordingId(recording.id)}
              >
                <div className="transcript-title">
                  {recording.title || `${recording.timestamp.toLocaleTimeString()} - ${formatDuration(recording.duration)}`}
                </div>
                {recording.title && (
                  <div className="transcript-time">
                    {recording.timestamp.toLocaleString()} • {formatDuration(recording.duration)}
                  </div>
                )}
                <div className="transcript-status">
                  {recording.summary ? '✓ Summary' : recording.isGeneratingSummary ? '⏳ Summarizing...' : ''}
                </div>
              </div>
              <button
                className="delete-button"
                onClick={(e) => {
                  e.stopPropagation();
                  deleteRecording(recording.id);
                }}
                title="Delete transcript"
              >
                🗑️
              </button>
            </li>
          ))}
        </ul>
      )}
    </aside>
  );

  // Show setup screen for non-ready states
  if (appState === "checking" || appState === "downloading_models" || appState === "initializing" || appState === "error") {
    return (
      <main className="container">
        <header className="app-header">
          <h1>🎙️ Chronara</h1>
          <p>Local Meeting Transcription & Summarization</p>
        </header>
        {renderSetupScreen()}
      </main>
    );
  }

  return (
    <main className="container">
      <header className="app-header">
        <h1>🎙️ Chronara</h1>
        <p>Local Meeting Transcription & Summarization</p>
      </header>

      <div className="app-layout">
        {/* Left sidebar with recordings list */}
        {renderRecordingsList()}

        {/* Main content area */}
        <div className="main-content">
          {renderRecordingControls()}

          {/* Display selected recording or active recording */}
          {displayedRecording && (
            <div className="transcript-details">
              <div className="transcript-header">
                <h2>{displayedRecording.title || `Transcript from ${displayedRecording.timestamp.toLocaleString()}`}</h2>
                <div className="transcript-actions">
                  <button
                    className="secondary-button"
                    onClick={() => downloadTranscript(displayedRecording.id)}
                  >
                    📄 Download Transcript
                  </button>
                  {!displayedRecording.summary && !displayedRecording.isGeneratingSummary && (
                    <button
                      className="primary-button"
                      onClick={() => generateSummary(displayedRecording.id)}
                    >
                      ✨ Generate Summary
                    </button>
                  )}
                  {displayedRecording.summary && (
                    <>
                      <button
                        className="secondary-button"
                        onClick={() => copyToClipboard(displayedRecording.summary!)}
                      >
                        📋 Copy Summary
                      </button>
                      <button
                        className="secondary-button"
                        onClick={() => downloadSummary(displayedRecording.id)}
                      >
                        💾 Download Summary
                      </button>
                    </>
                  )}
                </div>
              </div>

              <div className="content-grid">
                <TranscriptDisplay segments={displayedRecording.transcriptSegments} />
                <SummaryDisplay
                  summary={displayedRecording.summary}
                  isLoading={displayedRecording.isGeneratingSummary}
                  progress={displayedRecording.summaryProgress}
                />
              </div>
            </div>
          )}

          {!displayedRecording && recordings.length === 0 && appState === "ready" && (
            <div className="empty-state">
              <p>Click "Start Transcribing" to begin transcribing your first meeting!</p>
            </div>
          )}
        </div>
      </div>

      <BackendLogs isVisible={showLogs} onToggle={() => setShowLogs(!showLogs)} />
    </main>
  );
}

export default App;
