# app/modules/marketplace/tasks/import_tasks.py
"""
Celery tasks for marketplace product and order imports.

Includes:
- CSV product import from marketplace feeds
- Historical order imports from Letzshop API
"""

import asyncio
import logging
from collections.abc import Callable
from datetime import UTC, datetime

from app.core.celery_config import celery_app
from app.modules.marketplace.models import (
    LetzshopHistoricalImportJob,
    MarketplaceImportJob,
)
from app.modules.marketplace.services.letzshop import (
    LetzshopClientError,
    LetzshopCredentialsService,
    LetzshopOrderService,
)
from app.modules.messaging.services.admin_notification_service import (
    admin_notification_service,
)
from app.modules.task_base import ModuleTask
from app.modules.tenancy.models import Store
from app.utils.csv_processor import CSVProcessor

logger = logging.getLogger(__name__)


def _get_credentials_service(db) -> LetzshopCredentialsService:
    """Create a credentials service instance."""
    return LetzshopCredentialsService(db)


def _get_order_service(db) -> LetzshopOrderService:
    """Create an order service instance."""
    return LetzshopOrderService(db)


@celery_app.task(
    bind=True,
    base=ModuleTask,
    name="app.modules.marketplace.tasks.import_tasks.process_marketplace_import",
    max_retries=3,
    default_retry_delay=60,
    autoretry_for=(Exception,),
    retry_backoff=True,
    retry_backoff_max=600,
    retry_jitter=True,
)
def process_marketplace_import(
    self,
    job_id: int,
    url: str,
    marketplace: str,
    store_id: int,
    batch_size: int = 1000,
    language: str = "en",
):
    """
    Celery task to process marketplace CSV import.

    Args:
        job_id: ID of the MarketplaceImportJob record
        url: URL to the CSV file
        marketplace: Name of the marketplace (e.g., 'Letzshop')
        store_id: ID of the store
        batch_size: Number of rows to process per batch
        language: Language code for translations (default: 'en')

    Returns:
        dict: Import results with counts
    """
    csv_processor = CSVProcessor()

    with self.get_db() as db:
        # Get the import job
        job = db.query(MarketplaceImportJob).filter(MarketplaceImportJob.id == job_id).first()
        if not job:
            logger.error(f"Import job {job_id} not found")
            return {"error": f"Import job {job_id} not found"}

        # Store Celery task ID on job
        job.celery_task_id = self.request.id

        # Get store information
        store = db.query(Store).filter(Store.id == store_id).first()
        if not store:
            logger.error(f"Store {store_id} not found for import job {job_id}")
            job.status = "failed"
            job.error_message = f"Store {store_id} not found"
            job.completed_at = datetime.now(UTC)
            return {"error": f"Store {store_id} not found"}

        # Update job status
        job.status = "processing"
        job.started_at = datetime.now(UTC)

        logger.info(
            f"Processing import: Job {job_id}, Marketplace: {marketplace}, "
            f"Store: {store.name} ({store.store_code}), Language: {language}"
        )

        try:
            # Run the async CSV processor in a sync context
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                result = loop.run_until_complete(
                    csv_processor.process_marketplace_csv_from_url(
                        url=url,
                        marketplace=marketplace,
                        store_name=store.name,
                        batch_size=batch_size,
                        db=db,
                        language=language,
                        import_job_id=job_id,
                    )
                )
            finally:
                loop.close()

            # Update job with results
            job.status = "completed"
            job.completed_at = datetime.now(UTC)
            job.imported_count = result["imported"]
            job.updated_count = result["updated"]
            job.error_count = result.get("errors", 0)
            job.total_processed = result["total_processed"]

            if result.get("errors", 0) > 0:
                job.status = "completed_with_errors"
                job.error_message = f"{result['errors']} rows had errors"

                # Notify admin if error count is significant
                if result.get("errors", 0) >= 5:
                    admin_notification_service.notify_import_failure(
                        db=db,
                        store_name=store.name,
                        job_id=job_id,
                        error_message=f"Import completed with {result['errors']} errors out of {result['total_processed']} rows",
                        store_id=store_id,
                    )

            logger.info(
                f"Import job {job_id} completed: "
                f"imported={result['imported']}, updated={result['updated']}, "
                f"errors={result.get('errors', 0)}"
            )

            return {
                "job_id": job_id,
                "imported": result["imported"],
                "updated": result["updated"],
                "errors": result.get("errors", 0),
                "total_processed": result["total_processed"],
            }

        except Exception as e:
            logger.error(f"Import job {job_id} failed: {e}", exc_info=True)
            job.status = "failed"
            job.error_message = str(e)[:500]  # Truncate long errors
            job.completed_at = datetime.now(UTC)

            # Create admin notification for import failure
            admin_notification_service.notify_import_failure(
                db=db,
                store_name=store.name,
                job_id=job_id,
                error_message=str(e)[:200],
                store_id=store_id,
            )

            raise  # Re-raise for Celery retry


@celery_app.task(
    bind=True,
    base=ModuleTask,
    name="app.modules.marketplace.tasks.import_tasks.process_historical_import",
    max_retries=2,
    default_retry_delay=120,
    autoretry_for=(Exception,),
    retry_backoff=True,
)
def process_historical_import(self, job_id: int, store_id: int):
    """
    Celery task for historical order import with progress tracking.

    Imports both confirmed and unconfirmed orders from Letzshop API,
    updating job progress in the database for frontend polling.

    Args:
        job_id: ID of the LetzshopHistoricalImportJob record
        store_id: ID of the store to import orders for

    Returns:
        dict: Import statistics
    """
    with self.get_db() as db:
        # Get the import job
        job = (
            db.query(LetzshopHistoricalImportJob)
            .filter(LetzshopHistoricalImportJob.id == job_id)
            .first()
        )
        if not job:
            logger.error(f"Historical import job {job_id} not found")
            return {"error": f"Job {job_id} not found"}

        # Store Celery task ID
        job.celery_task_id = self.request.id

        try:
            # Mark as started
            job.status = "fetching"
            job.started_at = datetime.now(UTC)

            creds_service = _get_credentials_service(db)
            order_service = _get_order_service(db)

            # Create progress callback for fetching
            def fetch_progress_callback(page: int, total_fetched: int):
                """Update fetch progress in database."""
                job.current_page = page
                job.shipments_fetched = total_fetched

            # Create progress callback for processing
            def create_processing_callback(
                phase: str,
            ) -> Callable[[int, int, int, int], None]:
                """Create a processing progress callback for a phase."""

                def callback(processed: int, imported: int, updated: int, skipped: int):
                    job.orders_processed = processed
                    job.orders_imported = imported
                    job.orders_updated = updated
                    job.orders_skipped = skipped

                return callback

            with creds_service.create_client(store_id) as client:
                # ================================================================
                # Phase 1: Import confirmed orders
                # ================================================================
                job.current_phase = "confirmed"
                job.current_page = 0
                job.shipments_fetched = 0

                logger.info(f"Job {job_id}: Fetching confirmed shipments for store {store_id}")

                confirmed_shipments = client.get_all_shipments_paginated(
                    state="confirmed",
                    page_size=50,
                    progress_callback=fetch_progress_callback,
                )

                logger.info(f"Job {job_id}: Fetched {len(confirmed_shipments)} confirmed shipments")

                # Process confirmed shipments
                job.status = "processing"
                job.orders_processed = 0
                job.orders_imported = 0
                job.orders_updated = 0
                job.orders_skipped = 0

                confirmed_stats = order_service.import_historical_shipments(
                    store_id=store_id,
                    shipments=confirmed_shipments,
                    match_products=True,
                    progress_callback=create_processing_callback("confirmed"),
                )

                # Store confirmed stats
                job.confirmed_stats = {
                    "total": confirmed_stats["total"],
                    "imported": confirmed_stats["imported"],
                    "updated": confirmed_stats["updated"],
                    "skipped": confirmed_stats["skipped"],
                    "products_matched": confirmed_stats["products_matched"],
                    "products_not_found": confirmed_stats["products_not_found"],
                }
                job.products_matched = confirmed_stats["products_matched"]
                job.products_not_found = confirmed_stats["products_not_found"]

                logger.info(
                    f"Job {job_id}: Confirmed phase complete - "
                    f"imported={confirmed_stats['imported']}, "
                    f"updated={confirmed_stats['updated']}, "
                    f"skipped={confirmed_stats['skipped']}"
                )

                # ================================================================
                # Phase 2: Import unconfirmed (pending) orders
                # ================================================================
                job.current_phase = "unconfirmed"
                job.status = "fetching"
                job.current_page = 0
                job.shipments_fetched = 0

                logger.info(f"Job {job_id}: Fetching unconfirmed shipments for store {store_id}")

                unconfirmed_shipments = client.get_all_shipments_paginated(
                    state="unconfirmed",
                    page_size=50,
                    progress_callback=fetch_progress_callback,
                )

                logger.info(
                    f"Job {job_id}: Fetched {len(unconfirmed_shipments)} unconfirmed shipments"
                )

                # Process unconfirmed shipments
                job.status = "processing"
                job.orders_processed = 0

                unconfirmed_stats = order_service.import_historical_shipments(
                    store_id=store_id,
                    shipments=unconfirmed_shipments,
                    match_products=True,
                    progress_callback=create_processing_callback("unconfirmed"),
                )

                # Store unconfirmed stats
                job.declined_stats = {
                    "total": unconfirmed_stats["total"],
                    "imported": unconfirmed_stats["imported"],
                    "updated": unconfirmed_stats["updated"],
                    "skipped": unconfirmed_stats["skipped"],
                    "products_matched": unconfirmed_stats["products_matched"],
                    "products_not_found": unconfirmed_stats["products_not_found"],
                }

                # Add to cumulative product matching stats
                job.products_matched += unconfirmed_stats["products_matched"]
                job.products_not_found += unconfirmed_stats["products_not_found"]

                logger.info(
                    f"Job {job_id}: Unconfirmed phase complete - "
                    f"imported={unconfirmed_stats['imported']}, "
                    f"updated={unconfirmed_stats['updated']}, "
                    f"skipped={unconfirmed_stats['skipped']}"
                )

                # ================================================================
                # Complete
                # ================================================================
                job.status = "completed"
                job.completed_at = datetime.now(UTC)

                # Update credentials sync status
                creds_service.update_sync_status(store_id, "success", None)

                logger.info(f"Job {job_id}: Historical import completed successfully")

                return {
                    "job_id": job_id,
                    "confirmed": confirmed_stats,
                    "unconfirmed": unconfirmed_stats,
                }

        except LetzshopClientError as e:
            logger.error(f"Job {job_id}: Letzshop API error: {e}")
            job.status = "failed"
            job.error_message = f"Letzshop API error: {e}"
            job.completed_at = datetime.now(UTC)

            # Get store name for notification
            order_service = _get_order_service(db)
            store = order_service.get_store(store_id)
            store_name = store.name if store else f"Store {store_id}"

            # Create admin notification
            admin_notification_service.notify_order_sync_failure(
                db=db,
                store_name=store_name,
                error_message=f"Historical import failed: {str(e)[:150]}",
                store_id=store_id,
            )

            creds_service = _get_credentials_service(db)
            creds_service.update_sync_status(store_id, "failed", str(e))
            raise  # Re-raise for Celery retry

        except Exception as e:
            logger.error(f"Job {job_id}: Unexpected error: {e}", exc_info=True)
            job.status = "failed"
            job.error_message = str(e)[:500]
            job.completed_at = datetime.now(UTC)

            # Get store name for notification
            order_service = _get_order_service(db)
            store = order_service.get_store(store_id)
            store_name = store.name if store else f"Store {store_id}"

            # Create admin notification
            admin_notification_service.notify_critical_error(
                db=db,
                error_type="Historical Import",
                error_message=f"Import job {job_id} failed for {store_name}: {str(e)[:150]}",
                details={"job_id": job_id, "store_id": store_id, "store_name": store_name},
            )

            raise  # Re-raise for Celery retry
