"""File upload and parsing service."""

import csv
import io
import tempfile
from pathlib import Path
from typing import Dict, List, Optional

from werkzeug.datastructures import FileStorage

from words_to_readlang.parsers import get_parser, list_parsers

from ..database import db
from ..models import Entry, Upload
from .validation_service import ValidationService


class UploadService:
    """Service for handling file uploads and parsing."""

    @staticmethod
    def extract_languagereactor_metadata(file_path: Path) -> List[Dict[str, str]]:
        """Extract Language Reactor metadata from TSV file.

        Args:
            file_path: Path to Language Reactor TSV export file

        Returns:
            List of metadata dictionaries with inflected_form, example_translation_en,
            word_type, pos_tag, and source_info for each entry
        """
        metadata_list = []

        with open(file_path, encoding="utf-8") as f:
            content = f.read()

        # Parse as tab-separated
        reader = csv.reader(io.StringIO(content), delimiter="\t", quotechar='"')

        for cols in reader:
            # Skip rows that don't start with WORD| marker
            if not cols or not cols[0].startswith("WORD|"):
                continue

            # Extract metadata from Language Reactor columns
            metadata = {
                "word_type": cols[1].strip() if len(cols) > 1 else "",
                "example_translation_en": cols[3].strip().strip('"') if len(cols) > 3 else "",
                "inflected_form": cols[4].strip() if len(cols) > 4 else "",
                "pos_tag": cols[6].strip() if len(cols) > 6 else "",
                # Store source info from columns 9+ as a simple string
                "source_info": "\t".join(cols[9:]) if len(cols) > 9 else "",
            }
            metadata_list.append(metadata)

        return metadata_list

    @staticmethod
    def detect_format(file_path: Path) -> Optional[str]:
        """Attempt to auto-detect file format.

        Args:
            file_path: Path to uploaded file

        Returns:
            Detected format name ("pod101" or "languagereactor") or None
        """
        try:
            # Try to read first few lines
            with open(file_path, "rb") as f:
                first_bytes = f.read(1000)

            # Check for UTF-16 BOM (Pod101 signature)
            if first_bytes.startswith(b"\xff\xfe") or first_bytes.startswith(
                b"\xfe\xff"
            ):
                return "pod101"

            # Decode and check content
            try:
                content = first_bytes.decode("utf-8")
            except UnicodeDecodeError:
                # Might be UTF-16
                try:
                    content = first_bytes.decode("utf-16")
                    return "pod101"
                except UnicodeDecodeError:
                    return None

            # Check for Pod101 header
            if "Word,English" in content or "Word\tEnglish" in content:
                return "pod101"

            # Check for Language Reactor signature (tab-separated with WORD| marker)
            if "\t" in content and "WORD|" in content:
                return "languagereactor"

            # Default to Pod101 for CSV-like files
            if "," in content:
                return "pod101"

            return None

        except Exception:
            return None

    @staticmethod
    def process_upload(
        file: FileStorage,
        session_id: str,
        format_name: Optional[str] = None,
        auto_detect: bool = False,
        source_language: str = "fin",
        target_language: str = "eng",
    ) -> Upload:
        """Process uploaded file and create entries in database.

        Args:
            file: Uploaded file object
            session_id: Session ID to associate upload with
            format_name: Parser format name (pod101, languagereactor, lr)
            auto_detect: Whether to auto-detect format
            source_language: Source language code (e.g., 'fin', 'swe')
            target_language: Target language code (e.g., 'eng')

        Returns:
            Upload object with entries

        Raises:
            ValueError: If format is unknown or file cannot be parsed
        """
        # Save file to temporary location
        temp_dir = Path(tempfile.mkdtemp())
        temp_path = temp_dir / file.filename

        try:
            file.save(str(temp_path))

            # Auto-detect format if requested
            if auto_detect:
                detected_format = UploadService.detect_format(temp_path)
                if not detected_format:
                    raise ValueError(
                        "Could not auto-detect file format. Please specify format manually."
                    )
                format_name = detected_format

            if not format_name:
                raise ValueError("Format name is required")

            # Validate format exists
            available_parsers = list_parsers()
            if format_name not in available_parsers:
                raise ValueError(
                    f"Unknown format: {format_name}. Available: {', '.join(available_parsers.keys())}"
                )

            # Parse file using library parser
            parser = get_parser(format_name)
            lib_entries = parser.parse(temp_path)

            if not lib_entries:
                raise ValueError("No entries found in file")

            # Extract Language Reactor metadata if applicable
            lr_metadata = []
            if format_name in ("languagereactor", "lr"):
                lr_metadata = UploadService.extract_languagereactor_metadata(temp_path)

            # Create upload record
            upload = Upload(
                session_id=session_id,
                filename=file.filename,
                file_format=format_name,
                entry_count=len(lib_entries),
                tatoeba_enabled=False,
                source_language=source_language,
                target_language=target_language,
            )
            db.session.add(upload)
            db.session.flush()  # Get upload.id

            # Create entry records with validation
            for idx, lib_entry in enumerate(lib_entries):
                # Validate entry
                first_word = lib_entry.first_synonym
                example = lib_entry.example.strip()
                has_example = bool(example)
                word_in_example = (
                    first_word.lower() in example.lower() if has_example else False
                )

                if not has_example:
                    status = "invalid_no_example"
                elif not word_in_example:
                    status = "invalid_word_missing"
                else:
                    status = "valid"

                # Get Language Reactor metadata for this entry
                metadata = {}
                if lr_metadata and idx < len(lr_metadata):
                    metadata = lr_metadata[idx]

                # Create entry
                entry = Entry(
                    upload_id=upload.id,
                    original_word=lib_entry.word,
                    original_translation=lib_entry.translation,
                    original_example=example,
                    validation_status=status,
                    # Language Reactor metadata (optional)
                    inflected_form=metadata.get("inflected_form", None),
                    example_translation_en=metadata.get("example_translation_en", None),
                    word_type=metadata.get("word_type", None),
                    pos_tag=metadata.get("pos_tag", None),
                    source_info=metadata.get("source_info", None),
                )
                db.session.add(entry)

            db.session.commit()
            return upload

        except Exception as e:
            db.session.rollback()
            raise e

        finally:
            # Clean up temp file
            if temp_path.exists():
                temp_path.unlink()
            if temp_dir.exists():
                temp_dir.rmdir()
