"""
Password exporters for various formats (Chrome, Apple, JSON).
"""
import csv
import json
import logging
from typing import Iterator
from onepasswd.models import PasswordEntry
from onepasswd import crypto

logger = logging.getLogger(__name__)


class BaseExporter:
    """Base class for exporters."""
    
    def __init__(self):
        pass
    
    def export(self, items: Iterator[PasswordEntry], output_path: str, master_password: str):
        """
        Export password entries to file.
        
        Args:
            items: Iterator of PasswordEntry objects
            output_path: Output file path
            master_password: Master password for decryption
        """
        raise NotImplementedError


class ChromeExporter(BaseExporter):
    """
    Export to Chrome CSV format.

    Chrome supports: name, url, username, password, note
    Additional metadata (description, category, tags, keywords) goes in note field.
    """

    FIELDS = ['name', 'url', 'username', 'password', 'note']

    def export(self, items: Iterator[PasswordEntry], output_path: str, master_password: str):
        """Export to Chrome CSV format."""
        skipped = []
        with open(output_path, 'w', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=self.FIELDS, quoting=csv.QUOTE_ALL)
            writer.writeheader()

            for item in items:
                # Infer metadata if not present
                title, url = item.infer_metadata()
                entry_name = title or item.entries[0]

                # Decrypt password
                try:
                    password = crypto.decrypt_passwd(item.passwd, master_password)
                except UnicodeDecodeError as e:
                    skipped.append(entry_name)
                    logger.warning(f"Skipped entry '{entry_name}' - cannot decrypt password (corrupted or wrong master password): {e}")
                    continue

                # Skip entries with newlines in password (CSV format doesn't support them)
                if '\n' in password or '\r' in password:
                    skipped.append(entry_name)
                    logger.warning(f"Skipped entry '{entry_name}' - password contains newlines")
                    continue

                # Build note like Apple: description, category, tags, keywords
                notes_parts = []
                if item.description:
                    notes_parts.append(item.description)
                if item.category:
                    notes_parts.append(f"\nCategory: {item.category}")
                if item.tags:
                    notes_parts.append(f"\nTags: {', '.join(item.tags)}")
                if item.entries:
                    notes_parts.append(f"\nKeywords: {' '.join(item.entries)}")
                note = ''.join(notes_parts) if notes_parts else ''

                writer.writerow({
                    'name': title,
                    'url': url,
                    'username': item.username or '',
                    'password': password,
                    'note': note
                })

        if skipped:
            print(f"Warning: Skipped {len(skipped)} entries: {', '.join(skipped)}")


class AppleKeychainExporter(BaseExporter):
    """
    Export to Apple Keychain CSV format.

    Apple requires: Title, URL, Username, Password, Notes, OTPAuth
    Note: Additional metadata (category, tags, keywords) is included in Notes field.
    """

    FIELDS = ['Title', 'URL', 'Username', 'Password', 'Notes', 'OTPAuth']

    def export(self, items: Iterator[PasswordEntry], output_path: str, master_password: str):
        """Export to Apple Keychain CSV format."""
        skipped = []
        with open(output_path, 'w', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=self.FIELDS, quoting=csv.QUOTE_ALL)
            writer.writeheader()

            for item in items:
                # Infer metadata if not present
                title, url = item.infer_metadata()
                entry_name = title or item.entries[0]

                # Decrypt password
                try:
                    password = crypto.decrypt_passwd(item.passwd, master_password)
                except UnicodeDecodeError as e:
                    skipped.append(entry_name)
                    logger.warning(f"Skipped entry '{entry_name}' - cannot decrypt password (corrupted or wrong master password): {e}")
                    continue

                # Skip entries with newlines in password (CSV format doesn't support them)
                if '\n' in password or '\r' in password:
                    skipped.append(entry_name)
                    logger.warning(f"Skipped entry '{entry_name}' - password contains newlines")
                    continue

                # Build comprehensive notes with all missing fields
                notes_parts = []

                if item.description:
                    notes_parts.append(item.description)

                if item.category:
                    notes_parts.append(f"\nCategory: {item.category}")

                if item.tags:
                    notes_parts.append(f"\nTags: {', '.join(item.tags)}")

                if item.entries:
                    notes_parts.append(f"\nKeywords: {' '.join(item.entries)}")

                notes = ''.join(notes_parts) if notes_parts else ''

                writer.writerow({
                    'Title': title,
                    'URL': url,
                    'Username': item.username or '',
                    'Password': password,
                    'Notes': notes,
                    'OTPAuth': ''  # Not supported yet
                })

        if skipped:
            print(f"Warning: Skipped {len(skipped)} entries: {', '.join(skipped)}")


class JSONExporter(BaseExporter):
    """
    Export to JSON format with all fields (encrypted passwords).
    
    This format preserves all metadata and can be used for backup/restore.
    """
    
    def export(self, items: Iterator[PasswordEntry], output_path: str, master_password: str = None):
        """
        Export to JSON format.
        
        Args:
            items: Iterator of PasswordEntry objects
            output_path: Output file path
            master_password: If provided, passwords will be decrypted
        """
        entries = []
        
        for item in items:
            data = item.to_dict()
            
            # Optionally decrypt password
            if master_password:
                data['password_decrypted'] = crypto.decrypt_passwd(item.passwd, master_password)
            
            entries.append(data)
        
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(entries, f, indent=2, ensure_ascii=False)


class PlainTextExporter(BaseExporter):
    """
    Export to plain text format (human-readable).
    
    Format:
        Title: Example
        URL: https://example.com
        Username: user@example.com
        Password: ********
        Description: Notes here
        ---
    """
    
    def export(self, items: Iterator[PasswordEntry], output_path: str, master_password: str):
        """Export to plain text format."""
        with open(output_path, 'w', encoding='utf-8') as f:
            for item in items:
                # Infer metadata if not present
                title, url = item.infer_metadata()
                
                # Decrypt password
                password = crypto.decrypt_passwd(item.passwd, master_password)
                
                f.write(f"Title: {title}\n")
                if url:
                    f.write(f"URL: {url}\n")
                if item.username:
                    f.write(f"Username: {item.username}\n")
                f.write(f"Password: {password}\n")
                if item.description:
                    f.write(f"Description: {item.description}\n")
                if item.category:
                    f.write(f"Category: {item.category}\n")
                if item.tags:
                    f.write(f"Tags: {', '.join(item.tags)}\n")
                f.write(f"Keywords: {' '.join(item.entries)}\n")
                f.write(f"Created: {item.created_time}\n")
                f.write(f"Modified: {item.modified_time}\n")
                f.write("---\n\n")


def get_exporter(format_name: str) -> BaseExporter:
    """
    Get exporter instance by format name.
    
    Args:
        format_name: 'chrome', 'apple', 'json', or 'text'
        
    Returns:
        Exporter instance
        
    Raises:
        ValueError: If format is not supported
    """
    exporters = {
        'chrome': ChromeExporter,
        'apple': AppleKeychainExporter,
        'json': JSONExporter,
        'text': PlainTextExporter,
    }
    
    if format_name not in exporters:
        raise ValueError(f"Unsupported format: {format_name}. "
                        f"Available: {', '.join(exporters.keys())}")
    
    return exporters[format_name]()
