"""Native verify command implementation."""

import re
from dataclasses import dataclass
from datetime import date
from pathlib import Path
from typing import Optional, Tuple, List

from ontos.core.staleness import (
    normalize_describes,
    check_staleness,
)
from ontos.core.context import SessionContext
from ontos.io.config import load_project_config
from ontos.io.files import find_project_root, load_documents, load_frontmatter
from ontos.io.scan_scope import collect_scoped_documents, resolve_scan_scope
from ontos.io.yaml import parse_frontmatter_content
from ontos.ui.output import OutputHandler


@dataclass
class VerifyOptions:
    """Options for verify command."""
    path: Optional[Path] = None
    all: bool = False
    date: Optional[str] = None  # YYYY-MM-DD format
    quiet: bool = False
    json_output: bool = False
    scope: Optional[str] = None


def find_stale_documents_list(scope: Optional[str] = None) -> List[dict]:
    """Find all documents with stale describes fields using new architecture symbols."""
    from ontos.core.curation import load_ontosignore

    root = find_project_root()
    config = load_project_config(repo_root=root)
    effective_scope = resolve_scan_scope(scope, config.scanning.default_scope)
    ignore_patterns = load_ontosignore(root)
    files = collect_scoped_documents(
        root,
        config,
        effective_scope,
        base_skip_patterns=ignore_patterns,
    )
    load_result = load_documents(files, parse_frontmatter_content)
    if load_result.has_fatal_errors or load_result.duplicate_ids:
        return [] # Caller will handle empty and check if it needs to fail
    
    # Build ID to path mapping for staleness checker
    id_to_path = {
        doc_id: str(doc.filepath) 
        for doc_id, doc in load_result.documents.items()
    }
    
    stale_docs = []
    for doc_id, doc in load_result.documents.items():
        describes = normalize_describes(doc.frontmatter.get('describes'))
        if not describes:
            continue
            
        staleness = check_staleness(
            doc_id=doc_id,
            doc_path=str(doc.filepath),
            describes=describes,
            describes_verified=doc.frontmatter.get('describes_verified'),
            id_to_path=id_to_path
        )
        
        if staleness and staleness.is_stale():
            stale_docs.append({
                'doc_id': doc_id,
                'filepath': str(doc.filepath),
                'staleness': staleness
            })
            
    return stale_docs


def update_describes_verified(
    filepath: Path,
    new_date: date,
    ctx: SessionContext,
    output: OutputHandler
) -> bool:
    """Update the describes_verified field in a document.
    
    Matches exact regex replacement logic from legacy script.
    """
    try:
        content = filepath.read_text(encoding='utf-8')
        
        if not content.startswith('---'):
            output.error(f"{filepath} has no frontmatter")
            return False
        
        parts = content.split('---', 2)
        if len(parts) < 3:
            output.error(f"Invalid frontmatter in {filepath}")
            return False
        
        frontmatter = parts[1]
        body = parts[2]
        date_str = new_date.isoformat()
        
        # Check if describes_verified already exists
        if re.search(r'^describes_verified:', frontmatter, re.MULTILINE):
            new_frontmatter = re.sub(
                r'^describes_verified:.*$',
                f'describes_verified: {date_str}',
                frontmatter,
                flags=re.MULTILINE
            )
        else:
            # Add after describes field
            if re.search(r'^describes:', frontmatter, re.MULTILINE):
                new_frontmatter = re.sub(
                    r'^(describes:.*(?:\n  - .*)*)$',
                    f'\\1\ndescribes_verified: {date_str}',
                    frontmatter,
                    flags=re.MULTILINE
                )
            else:
                new_frontmatter = frontmatter.rstrip() + f'\ndescribes_verified: {date_str}\n'
        
        new_content = f'---{new_frontmatter}---{body}'
        ctx.buffer_write(filepath, new_content)
        return True
    except Exception as e:
        output.error(f"Error updating {filepath}: {e}")
        return False


def verify_document(path: Path, verify_date: str) -> Tuple[bool, str]:
    """Helper for verify command."""
    # Ensure path is Path object
    p = Path(path)
    root = find_project_root()
    ctx = SessionContext.from_repo(root)
    output = OutputHandler(quiet=True)
    
    try:
        dt = date.fromisoformat(verify_date)
    except ValueError:
        return False, "Invalid date format"
        
    success = update_describes_verified(p, dt, ctx, output)
    if success:
        try:
            ctx.commit()
        except Exception as exc:
            return False, f"Commit failed: {exc}"
        return True, "Verified"
    return False, "Failed"


def verify_all_interactive(verify_date: date, output: OutputHandler, scope: Optional[str] = None) -> int:
    """Interactively verify all stale documents."""
    root = find_project_root()
    from ontos.core.curation import load_ontosignore
    config = load_project_config(repo_root=root)
    effective_scope = resolve_scan_scope(scope, config.scanning.default_scope)
    ignore_patterns = load_ontosignore(root)
    files = collect_scoped_documents(
        root,
        config,
        effective_scope,
        base_skip_patterns=ignore_patterns,
    )
    load_result = load_documents(files, parse_frontmatter_content)
    
    if load_result.has_fatal_errors or load_result.duplicate_ids:
        for issue in load_result.issues:
            if issue.code in {"duplicate_id", "parse_error", "io_error"}:
                output.error(issue.message)
        return 1

    stale_docs = find_stale_documents_list(scope=scope)
    
    if not stale_docs:
        output.success("No stale documents found.")
        return 0
    
    updated = 0
    skipped = 0
    ctx = SessionContext.from_repo(root)
    
    for i, doc in enumerate(stale_docs, 1):
        staleness = doc['staleness']
        # Show up to 3 stale atoms
        stale_atoms_str = ", ".join([f"{a} changed {d}" for a, d in staleness.stale_atoms[:3]])
        
        output.plain(f"\n[{i}/{len(stale_docs)}] {doc['doc_id']}")
        output.detail(f"File: {doc['filepath']}")
        output.detail(f"Stale because: {stale_atoms_str}")
        
        try:
            response = input("      Verify as current? [y/N]: ").strip().lower()
        except (EOFError, KeyboardInterrupt):
            output.warning("Aborted")
            return 1
            
        if response == 'y':
            if update_describes_verified(Path(doc['filepath']), verify_date, ctx, output):
                output.success(f"Updated describes_verified to {verify_date}")
                updated += 1
            else:
                output.error("Failed to update")
        else:
            output.info("Skipped")
            skipped += 1
            
    if updated > 0:
        try:
            ctx.commit()
        except Exception as exc:
            output.error(f"Failed to commit verify updates: {exc}")
            return 1
        
    output.info(f"Done. Updated: {updated}, Skipped: {skipped}")
    return 0


def _run_verify_command(options: VerifyOptions) -> Tuple[int, str]:
    """Execute verify command."""
    output = OutputHandler(quiet=options.quiet)
    
    # Parse date
    verify_date = date.today()
    if options.date:
        try:
            verify_date = date.fromisoformat(options.date)
        except ValueError:
            output.error(f"Invalid date format: {options.date}. Use YYYY-MM-DD.")
            return 1, f"Invalid date format: {options.date}"

    if options.path:
        # Single file mode
        if not options.path.exists():
            output.error(f"File not found: {options.path}")
            return 1, f"File not found: {options.path}"
            
        root = find_project_root()
        ctx = SessionContext.from_repo(root)
        
        # Check if doc has describes
        fm, _ = load_frontmatter(options.path, parse_frontmatter_content)
        if not fm:
            output.error(f"Failed to parse frontmatter in {options.path}")
            return 1, "Parse failure"
            
        describes = normalize_describes(fm.get('describes'))
        if not describes:
            output.warning(f"{options.path} has no describes field, nothing to verify")
            return 0, "Nothing to verify"
            
        if update_describes_verified(options.path, verify_date, ctx, output):
            try:
                ctx.commit()
            except Exception as exc:
                output.error(f"Failed to commit verify updates: {exc}")
                return 1, f"Commit failed: {exc}"
            output.success(f"Updated describes_verified to {verify_date}")
            return 0, "Success"
        else:
            return 1, "Update failed"

    elif options.all:
        # Interactive all mode
        result = verify_all_interactive(verify_date, output, scope=options.scope)
        return result, "Interactive session ended"
        
    else:
        output.error("Specify a file path or use --all")
        return 1, "No target specified"


def verify_command(options: VerifyOptions) -> int:
    """Run verify command and return exit code only."""
    exit_code, _ = _run_verify_command(options)
    return exit_code
