import logging
import threading
from datetime import datetime
from pathlib import Path

from whisper_assistant.env import read_env
from whisper_assistant.paths import get_history_dir
from whisper_assistant import log_config  # Import logging configuration
from whisper_assistant.packages.audio_recorder import AudioRecorder
from whisper_assistant.packages.transcriber import Transcriber
from whisper_assistant.packages.keyboard_listener import KeyboardListener
from whisper_assistant.packages.notifications import Notifier

logger = logging.getLogger(__name__)

HISTORY_DIR = get_history_dir()


class WhisperApp:
    """Main application orchestrating keyboard listener, audio recorder, and transcriber."""

    def __init__(self):
        """Initialize the application components."""
        # Load config first (this loads from XDG config path)
        self.env = read_env()
        logger.info(f"init config: \n{self.env}")

        self.notifier = Notifier()
        self.recorder = AudioRecorder(notifier=self.notifier)
        self.transcriber = Transcriber()
        self.keyboard_listener = KeyboardListener()
        self.last_audio_file = None

    def stop_recording(self):
        """Stop the current recording."""
        if not self.recorder.is_recording():
            logger.debug("Stop command ignored: Not recording")
            return

        logger.info("Stopping recording...")
        self.recorder.stop_recording()

    def cancel_recording(self):
        """Cancel the current recording without transcribing."""
        if not self.recorder.is_recording():
            logger.debug("Cancel command ignored: Not recording")
            return

        logger.info("Cancelling recording...")
        self.recorder.cancel_recording()
        self.notifier.show_alert("Recording cancelled", "Whisper Assistant")
        self.notifier.play_sound("/System/Library/Sounds/Basso.aiff", volume=25)

    def toggle_recording(self):
        """Callback for toggle recording hotkey."""
        if self.recorder.is_recording():
            self.stop_recording()
        else:
            logger.info("Starting recording process...")
            # Run recording in a separate thread to avoid blocking the keyboard listener
            threading.Thread(target=self._recording_flow).start()

    def retry_transcription(self):
        """Callback for retry transcription hotkey."""
        if self.last_audio_file:
            logger.info(f"Retrying transcription of: {self.last_audio_file}")
            self.transcribe_file(
                self.last_audio_file, language=self.env.TRANSCRIPTION_LANGUAGE
            )
        else:
            logger.warning("No previous audio file to retry transcription")

    def _get_today_history_dir(self):
        """Get or create today's history directory."""
        today = datetime.now().strftime("%Y-%m-%d")
        history_path = HISTORY_DIR / today
        history_path.mkdir(parents=True, exist_ok=True)
        return history_path

    def _recording_flow(self):
        """
        Handles the recording process: record -> save -> transcribe.
        This runs in a separate thread.
        """
        # Get today's history directory
        history_dir = self._get_today_history_dir()

        # Generate timestamp folder name (HHMMSS format, no separators)
        timestamp = datetime.now().strftime("%H%M%S")
        entry_dir = history_dir / timestamp
        entry_dir.mkdir(parents=True, exist_ok=True)

        # Audio file path: history/YYYY-MM-DD/HHMMSS/recording.wav
        audio_path = entry_dir / "recording.wav"

        # This blocks until stop_recording() is called
        # The AudioRecorder will notify when recording actually starts
        file_path = self.recorder.start_recording(
            output_path=str(audio_path),
            notification_message=f"Recording with lang={self.env.TRANSCRIPTION_LANGUAGE}",
        )

        logger.info("Recording completed")

        if file_path:
            self.last_audio_file = file_path
            logger.info("Transcribing recording...")
            self.transcribe_file(file_path, language=self.env.TRANSCRIPTION_LANGUAGE)
        else:
            logger.info(
                "Recording completed but no file path was returned, skipping transcription"
            )

    def transcribe_file(self, audio_file_path, language):
        """Transcribe audio file and output the result."""
        try:
            text = self.transcriber.transcribe(audio_file_path, language=language)

            if text:
                self._output_transcription(text)
                # Save transcription to file
                self._save_transcription(audio_file_path, text)
                # Notify user that transcription is complete
                self.notify_completion()
            else:
                logger.info("Transcription returned empty result")

        except Exception as e:
            logger.error(f"Error during transcription: {e}", exc_info=True)

    def _save_transcription(self, audio_file_path, text):
        """Save transcription text to a file alongside the audio file."""
        try:
            # Save as transcription.txt in the same directory as recording.wav
            audio_path = Path(audio_file_path)
            transcription_path = audio_path.parent / "transcription.txt"
            with open(transcription_path, "w", encoding="utf-8") as f:
                f.write(text)
            logger.debug(f"Transcription saved to {transcription_path}")
        except Exception as e:
            logger.error(f"Error saving transcription: {e}", exc_info=True)

    def _output_transcription(self, text):
        """Output transcription based on configured output modes."""
        print(f"\n{'=' * 60}")
        print(f"Transcription:")
        print(f"{text}")
        print(f"{'=' * 60}\n")

        output = self.env.TRANSCRIPTION_OUTPUT

        if output.clipboard:
            try:
                import pyperclip

                pyperclip.copy(text)
                logger.info("Transcription copied to clipboard")
            except ImportError:
                logger.warning("pyperclip not available, skipping clipboard copy")

        if output.paste_on_cursor:
            try:
                from pynput.keyboard import Controller

                keyboard_controller = Controller()
                keyboard_controller.type(text)
                logger.info("Transcription typed at cursor position")
            except Exception as e:
                logger.error(f"Failed to type transcription at cursor: {e}")

    def notify_completion(self):
        self.notifier.show_alert("Transcription complete", "Whisper Assistant")
        self.notifier.play_sound("/System/Library/Sounds/Glass.aiff", volume=25)

    def run(self):
        """Start the application."""
        logger.info("Starting Whisper Assistant...")
        logger.info(f"Config: \n{self.env}")

        # Register hotkeys
        self.keyboard_listener.register_hotkey(
            self.env.TOGGLE_RECORDING_HOTKEY, self.toggle_recording
        )
        self.keyboard_listener.register_hotkey(
            self.env.RETRY_TRANSCRIPTION_HOTKEY, self.retry_transcription
        )
        self.keyboard_listener.register_hotkey(
            self.env.CANCEL_RECORDING_HOTKEY, self.cancel_recording
        )

        # Start listener and block main thread
        self.keyboard_listener.start()
        try:
            self.keyboard_listener.join()
        except KeyboardInterrupt:
            logger.info("Shutting down...")
            self.shutdown()

    def shutdown(self):
        """Clean shutdown of all components."""
        if self.recorder.is_recording():
            self.recorder.stop_recording()
        self.recorder.cleanup()  # Clean up PyAudio instance
        self.keyboard_listener.stop()


if __name__ == "__main__":
    app = WhisperApp()
    app.run()
