"""
Service for managing invoice file archival to Nextcloud.

Handles:
- Automatic archival after invoice confirmation + Dext send
- File retrieval from Nextcloud for re-processing
- Deleted file handling
"""
import os
import hashlib
import logging
from datetime import datetime
from typing import Tuple, Optional

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func

from models.invoice import Invoice, InvoiceStatus
from models.settings import KitchenSettings
from models.supplier import Supplier
from services.nextcloud_service import NextcloudService

logger = logging.getLogger(__name__)


class FileArchivalService:
    """Service for managing invoice file archival"""

    def __init__(self, db: AsyncSession, kitchen_id: int):
        self.db = db
        self.kitchen_id = kitchen_id

    async def get_settings(self) -> Optional[KitchenSettings]:
        """Get kitchen settings, with global credential overrides applied in memory."""
        result = await self.db.execute(
            select(KitchenSettings).where(KitchenSettings.kitchen_id == self.kitchen_id)
        )
        settings = result.scalar_one_or_none()
        if settings:
            from services.global_settings_service import apply_global_overrides
            await apply_global_overrides(settings)
        return settings

    async def is_ready_for_archival(self, invoice: Invoice) -> bool:
        """
        Check if invoice is ready to be archived to Nextcloud.

        Conditions:
        - Status is CONFIRMED
        - If Dext is enabled and auto-send is on, must have been sent
        - File is still local
        """
        if invoice.status != InvoiceStatus.CONFIRMED:
            return False

        if invoice.file_storage_location != "local":
            return False

        settings = await self.get_settings()
        if not settings or not settings.nextcloud_enabled:
            return False

        # Check Dext requirement - only if auto-send is enabled
        if settings.dext_email and settings.dext_auto_send_enabled:
            if not invoice.dext_sent_at:
                return False

        return True

    async def archive_invoice_file(self, invoice: Invoice) -> Tuple[bool, str]:
        """
        Archive invoice file to Nextcloud.

        Args:
            invoice: Invoice to archive

        Returns:
            (success, message or path)
        """
        settings = await self.get_settings()
        if not settings or not settings.nextcloud_enabled:
            return (False, "Nextcloud not enabled")

        if not all([settings.nextcloud_host, settings.nextcloud_username, settings.nextcloud_password]):
            return (False, "Nextcloud not configured")

        if invoice.file_storage_location != "local":
            return (False, "File already archived")

        if not os.path.exists(invoice.image_path):
            return (False, "Local file not found")

        try:
            # Get supplier name
            supplier_name = "Unknown"
            if invoice.supplier_id:
                result = await self.db.execute(
                    select(Supplier).where(Supplier.id == invoice.supplier_id)
                )
                supplier = result.scalar_one_or_none()
                if supplier:
                    supplier_name = supplier.name

            # Read file
            with open(invoice.image_path, 'rb') as f:
                file_content = f.read()

            # Generate hash from file content
            file_hash = hashlib.md5(file_content).hexdigest()

            # Initialize Nextcloud service
            nc = NextcloudService(
                settings.nextcloud_host,
                settings.nextcloud_username,
                settings.nextcloud_password,
                settings.nextcloud_base_path
            )

            # Generate path and filename
            dir_path = nc.generate_path(supplier_name, invoice.invoice_date)
            filename = nc.generate_filename(
                invoice.invoice_date,
                supplier_name,
                invoice.invoice_number,
                float(invoice.total) if invoice.total else 0,
                os.path.basename(invoice.image_path),
                file_hash
            )

            # Upload to Nextcloud
            success, result = await nc.upload_file(file_content, dir_path, filename)
            await nc.close()

            if not success:
                return (False, f"Upload failed: {result}")

            # Update invoice record
            invoice.original_local_path = invoice.image_path
            invoice.nextcloud_path = result
            invoice.file_storage_location = "nextcloud"
            invoice.archived_at = datetime.utcnow()

            await self.db.commit()

            logger.info(f"Archived invoice {invoice.id} to Nextcloud: {result}")

            # Delete local file if setting enabled
            if settings.nextcloud_delete_local and os.path.exists(invoice.image_path):
                try:
                    os.remove(invoice.image_path)
                    logger.info(f"Deleted local file for invoice {invoice.id}: {invoice.image_path}")
                except Exception as e:
                    logger.warning(f"Failed to delete local file {invoice.image_path}: {e}")

            return (True, result)

        except Exception as e:
            logger.error(f"Archival failed for invoice {invoice.id}: {e}")
            return (False, str(e))

    async def get_file_content(self, invoice: Invoice) -> Tuple[bool, bytes | str]:
        """
        Get file content, downloading from Nextcloud if needed.

        Used for OCR re-processing or file serving.

        Args:
            invoice: Invoice to get file for

        Returns:
            (success, file_bytes or error_message)
        """
        # Try local file first (image_path)
        if os.path.exists(invoice.image_path):
            with open(invoice.image_path, 'rb') as f:
                return (True, f.read())

        # Try original local path (if file was archived but local copy exists)
        if invoice.original_local_path and os.path.exists(invoice.original_local_path):
            with open(invoice.original_local_path, 'rb') as f:
                return (True, f.read())

        # Download from Nextcloud
        if invoice.file_storage_location == "nextcloud" and invoice.nextcloud_path:
            settings = await self.get_settings()
            if not settings or not settings.nextcloud_enabled:
                return (False, "Nextcloud not configured")

            if not all([settings.nextcloud_host, settings.nextcloud_username, settings.nextcloud_password]):
                return (False, "Nextcloud credentials not set")

            nc = NextcloudService(
                settings.nextcloud_host,
                settings.nextcloud_username,
                settings.nextcloud_password,
                ""  # Path already includes base
            )

            success, result = await nc.download_file(invoice.nextcloud_path)
            await nc.close()

            if success and isinstance(result, bytes):
                # Cache locally for future use
                try:
                    os.makedirs(os.path.dirname(invoice.image_path), exist_ok=True)
                    with open(invoice.image_path, 'wb') as f:
                        f.write(result)
                except Exception as e:
                    logger.warning(f"Failed to cache file locally: {e}")

            return (success, result)

        return (False, "File not found")

    async def handle_invoice_deletion(self, invoice: Invoice) -> Tuple[bool, str]:
        """
        Handle file when invoice is being deleted.

        If on Nextcloud, copy to deleted folder.

        Args:
            invoice: Invoice being deleted

        Returns:
            (success, message)
        """
        if invoice.file_storage_location == "nextcloud" and invoice.nextcloud_path:
            settings = await self.get_settings()
            if settings and settings.nextcloud_enabled:
                try:
                    # Get supplier name for deleted folder
                    supplier_name = "Unknown"
                    if invoice.supplier_id:
                        result = await self.db.execute(
                            select(Supplier).where(Supplier.id == invoice.supplier_id)
                        )
                        supplier = result.scalar_one_or_none()
                        if supplier:
                            supplier_name = supplier.name

                    nc = NextcloudService(
                        settings.nextcloud_host,
                        settings.nextcloud_username,
                        settings.nextcloud_password,
                        settings.nextcloud_base_path
                    )

                    # Copy to deleted folder
                    original_filename = os.path.basename(invoice.nextcloud_path)
                    success, msg = await nc.copy_to_deleted(
                        invoice.nextcloud_path,
                        supplier_name,
                        original_filename
                    )
                    await nc.close()

                    if not success:
                        logger.warning(f"Failed to copy to deleted folder: {msg}")

                    return (True, "File moved to deleted folder")

                except Exception as e:
                    logger.error(f"Error handling file deletion: {e}")

        # Local file deletion
        if invoice.image_path and os.path.exists(invoice.image_path):
            try:
                os.remove(invoice.image_path)
            except Exception as e:
                logger.warning(f"Failed to delete local file: {e}")

        # Also try to delete original local path if different
        if invoice.original_local_path and invoice.original_local_path != invoice.image_path:
            if os.path.exists(invoice.original_local_path):
                try:
                    os.remove(invoice.original_local_path)
                except Exception as e:
                    logger.warning(f"Failed to delete original local file: {e}")

        return (True, "Local file deleted")

    async def get_archive_stats(self) -> dict:
        """
        Get archival statistics for this kitchen.

        Returns:
            dict with pending_count, archived_count, etc.
        """
        settings = await self.get_settings()

        # Count invoices ready for archival (confirmed + local)
        pending_query = select(func.count(Invoice.id)).where(
            Invoice.kitchen_id == self.kitchen_id,
            Invoice.status == InvoiceStatus.CONFIRMED,
            Invoice.file_storage_location == "local"
        )
        pending_result = await self.db.execute(pending_query)
        pending_count = pending_result.scalar() or 0

        # Count already archived invoices
        archived_query = select(func.count(Invoice.id)).where(
            Invoice.kitchen_id == self.kitchen_id,
            Invoice.file_storage_location == "nextcloud"
        )
        archived_result = await self.db.execute(archived_query)
        archived_count = archived_result.scalar() or 0

        # Count all invoices with local files (any status)
        local_query = select(func.count(Invoice.id)).where(
            Invoice.kitchen_id == self.kitchen_id,
            Invoice.file_storage_location == "local"
        )
        local_result = await self.db.execute(local_query)
        local_count = local_result.scalar() or 0

        return {
            "pending_count": pending_count,
            "archived_count": archived_count,
            "local_count": local_count,
            "nextcloud_enabled": settings.nextcloud_enabled if settings else False,
            "nextcloud_configured": bool(
                settings and settings.nextcloud_host and
                settings.nextcloud_username and settings.nextcloud_password
            ) if settings else False
        }

    async def archive_all_pending(self) -> Tuple[int, int, list]:
        """
        Archive all pending invoices to Nextcloud.

        Returns:
            (success_count, failed_count, error_messages)
        """
        settings = await self.get_settings()
        if not settings or not settings.nextcloud_enabled:
            return (0, 0, ["Nextcloud not enabled"])

        if not all([settings.nextcloud_host, settings.nextcloud_username, settings.nextcloud_password]):
            return (0, 0, ["Nextcloud not configured"])

        # Get all invoices ready for archival
        query = select(Invoice).where(
            Invoice.kitchen_id == self.kitchen_id,
            Invoice.status == InvoiceStatus.CONFIRMED,
            Invoice.file_storage_location == "local"
        )
        result = await self.db.execute(query)
        invoices = result.scalars().all()

        success_count = 0
        failed_count = 0
        errors = []

        for invoice in invoices:
            if await self.is_ready_for_archival(invoice):
                try:
                    success, msg = await self.archive_invoice_file(invoice)
                    if success:
                        success_count += 1
                    else:
                        failed_count += 1
                        errors.append(f"Invoice {invoice.id}: {msg}")
                except Exception as e:
                    failed_count += 1
                    errors.append(f"Invoice {invoice.id}: {str(e)}")

        return (success_count, failed_count, errors)
