"""SQLAlchemy database models for web interface."""

from datetime import datetime
from typing import Optional

from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship

from .database import db


class Session(db.Model):
    """User session tracked by magic phrase.

    Attributes:
        id: Unique session identifier (UUID)
        magic_phrase: Memorable 4-word phrase for session identification
        created_at: When session was created
        last_accessed_at: When session was last accessed
        expires_at: When session expires (30 days from creation)
        uploads: Related Upload objects
    """

    __tablename__ = "session"

    id = Column(String(36), primary_key=True)
    magic_phrase = Column(String(100), unique=True, nullable=False, index=True)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    last_accessed_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    expires_at = Column(DateTime, nullable=False)

    # Relationships
    uploads = relationship(
        "Upload", back_populates="session", cascade="all, delete-orphan"
    )

    def __repr__(self) -> str:
        return f"<Session {self.magic_phrase}>"


class Upload(db.Model):
    """File upload and conversion record.

    Attributes:
        id: Unique upload identifier
        session_id: Foreign key to Session
        filename: Original filename
        file_format: Parser format (pod101, languagereactor, lr)
        entry_count: Number of entries in upload
        uploaded_at: When file was uploaded
        tatoeba_enabled: Whether Tatoeba was enabled for this upload
        source_language: Source language code (e.g., 'fin', 'swe', 'spa')
        target_language: Target language code (e.g., 'eng', 'fra')
        session: Related Session object
        entries: Related Entry objects
    """

    __tablename__ = "upload"

    id = Column(Integer, primary_key=True)
    session_id = Column(String(36), ForeignKey("session.id"), nullable=False)
    filename = Column(String(255), nullable=False)
    file_format = Column(String(50), nullable=False)
    entry_count = Column(Integer, nullable=False)
    uploaded_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    tatoeba_enabled = Column(Boolean, default=False, nullable=False)
    source_language = Column(String(10), nullable=False, default="fin")
    target_language = Column(String(10), nullable=False, default="eng")

    # Relationships
    session = relationship("Session", back_populates="uploads")
    entries = relationship(
        "Entry", back_populates="upload", cascade="all, delete-orphan"
    )

    def __repr__(self) -> str:
        return f"<Upload {self.filename} ({self.entry_count} entries)>"


class Entry(db.Model):
    """Vocabulary entry with validation status.

    Stores both original parsed values and user-edited values.
    Validation status tracks whether example contains the word.

    Attributes:
        id: Unique entry identifier
        upload_id: Foreign key to Upload
        original_word: Word as parsed from file
        original_translation: Translation as parsed from file
        original_example: Example sentence as parsed from file
        edited_word: User-edited word (optional)
        edited_translation: User-edited translation (optional)
        edited_example: User-edited example (optional)
        validation_status: valid, invalid_no_example, or invalid_word_missing
        tatoeba_attempted: Whether Tatoeba fetch was attempted
        tatoeba_result: Example sentence from Tatoeba (if fetched)
        inflected_form: Inflected form from Language Reactor (optional)
        example_translation_en: English translation of example from Language Reactor (optional)
        word_type: Word type (Word/Phrase) from Language Reactor (optional)
        pos_tag: Part-of-speech tag from Language Reactor (optional)
        source_info: Additional source metadata from Language Reactor (optional, JSON)
        created_at: When entry was created
        updated_at: When entry was last updated
        upload: Related Upload object
    """

    __tablename__ = "entry"

    id = Column(Integer, primary_key=True)
    upload_id = Column(Integer, ForeignKey("upload.id"), nullable=False)

    # Original parsed values
    original_word = Column(String(255), nullable=False)
    original_translation = Column(Text, nullable=False)
    original_example = Column(Text, nullable=False, default="")

    # User-edited values (nullable)
    edited_word = Column(String(255), nullable=True)
    edited_translation = Column(Text, nullable=True)
    edited_example = Column(Text, nullable=True)

    # Validation and Tatoeba
    validation_status = Column(String(50), nullable=False, default="valid")
    tatoeba_attempted = Column(Boolean, default=False, nullable=False)
    tatoeba_result = Column(Text, nullable=True)
    tatoeba_sentence_id = Column(Integer, nullable=True)
    example_license = Column(String(50), nullable=True)
    example_author = Column(String(100), nullable=True)
    example_source = Column(String(20), nullable=True)  # "tatoeba" or "leipzig"

    # Language Reactor metadata (for future Readlang format extensions)
    inflected_form = Column(String(255), nullable=True)  # Col 4
    example_translation_en = Column(Text, nullable=True)  # Col 3
    word_type = Column(String(50), nullable=True)  # Col 1 (Word/Phrase)
    pos_tag = Column(String(50), nullable=True)  # Col 6
    source_info = Column(Text, nullable=True)  # Col 9+ (JSON)

    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    updated_at = Column(DateTime, onupdate=datetime.utcnow, nullable=True)

    # Relationships
    upload = relationship("Upload", back_populates="entries")

    @property
    def current_word(self) -> str:
        """Return edited word if available, otherwise original."""
        return self.edited_word or self.original_word

    @property
    def current_translation(self) -> str:
        """Return edited translation if available, otherwise original."""
        return self.edited_translation or self.original_translation

    @property
    def current_example(self) -> str:
        """Return edited example if available, otherwise original."""
        return self.edited_example or self.original_example

    @property
    def is_valid(self) -> bool:
        """Check if entry has valid validation status."""
        return self.validation_status == "valid"

    @property
    def needs_example(self) -> bool:
        """Check if entry needs an example sentence."""
        return self.validation_status in ("invalid_no_example", "invalid_word_missing")

    def __repr__(self) -> str:
        return f"<Entry {self.current_word} ({self.validation_status})>"


class TatoebaCache(db.Model):
    """Cache for Tatoeba example sentences.

    Attributes:
        id: Unique cache identifier
        word: The word searched for
        source_language: Source language code (e.g., 'fin', 'swe')
        target_language: Target language code (e.g., 'eng')
        sentence_id: Tatoeba sentence ID
        sentence_text: The example sentence text
        license: License of the sentence (e.g., 'CC BY 2.0 FR')
        author: Username of sentence author on Tatoeba
        created_at: When this cache entry was created
    """

    __tablename__ = "tatoeba_cache"

    id = Column(Integer, primary_key=True)
    word = Column(String(255), nullable=False, index=True)
    source_language = Column(String(10), nullable=False, index=True)
    target_language = Column(String(10), nullable=False, index=True)
    sentence_id = Column(Integer, nullable=False)
    sentence_text = Column(Text, nullable=False)
    license = Column(String(50), nullable=False)
    author = Column(String(100), nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)

    def __repr__(self) -> str:
        return f"<TatoebaCache {self.word} ({self.source_language})>"


class LeipzigCache(db.Model):
    """Cache for Leipzig Corpora Collection example sentences.

    Leipzig results are language-only (no target language) and carry a
    source URL instead of an author name.

    Attributes:
        id: Unique cache identifier
        word: The word searched for
        source_language: Source language code (e.g., 'fin', 'deu')
        sentence_id: Leipzig sentence ID (string)
        sentence_text: The example sentence text
        source_url: URL of the original source document
        created_at: When this cache entry was created
    """

    __tablename__ = "leipzig_cache"

    id = Column(Integer, primary_key=True)
    word = Column(String(255), nullable=False, index=True)
    source_language = Column(String(10), nullable=False, index=True)
    sentence_id = Column(String(50), nullable=False)
    sentence_text = Column(Text, nullable=False)
    source_url = Column(String(500), nullable=True)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)

    def __repr__(self) -> str:
        return f"<LeipzigCache {self.word} ({self.source_language})>"
