# app/modules/marketplace/services/onboarding_service.py
"""
Store onboarding service.

Handles the 4-step mandatory onboarding wizard for new stores:
1. Merchant Profile Setup
2. Letzshop API Configuration
3. Product & Order Import (CSV feed URL configuration)
4. Order Sync (historical import with progress tracking)
"""

import logging
from datetime import UTC, datetime

from sqlalchemy.orm import Session

from app.modules.marketplace.exceptions import (
    OnboardingCsvUrlRequiredException,
    OnboardingNotFoundException,
    OnboardingStepOrderException,
    OnboardingSyncJobNotFoundException,
    OnboardingSyncNotCompleteException,
)
from app.modules.marketplace.models import (
    OnboardingStatus,
    OnboardingStep,
    StoreOnboarding,
)
from app.modules.marketplace.services.letzshop import (
    LetzshopCredentialsService,
    LetzshopOrderService,
)
from app.modules.tenancy.exceptions import StoreNotFoundException

logger = logging.getLogger(__name__)


class OnboardingService:
    """
    Service for managing store onboarding workflow.

    Provides methods for each onboarding step and progress tracking.
    """

    def __init__(self, db: Session):
        """
        Initialize the onboarding service.

        Args:
            db: SQLAlchemy database session.
        """
        self.db = db

    def _get_store(self, store_id: int):
        """Get store by ID via store service."""
        from app.modules.tenancy.services.store_service import store_service

        return store_service.get_store_by_id_optional(self.db, store_id)

    # =========================================================================
    # Onboarding CRUD
    # =========================================================================

    def get_onboarding(self, store_id: int) -> StoreOnboarding | None:
        """Get onboarding record for a store."""
        return (
            self.db.query(StoreOnboarding)
            .filter(StoreOnboarding.store_id == store_id)
            .first()
        )

    def get_onboarding_or_raise(self, store_id: int) -> StoreOnboarding:
        """Get onboarding record or raise OnboardingNotFoundException."""
        onboarding = self.get_onboarding(store_id)
        if onboarding is None:
            raise OnboardingNotFoundException(store_id)
        return onboarding

    def create_onboarding(self, store_id: int) -> StoreOnboarding:
        """
        Create a new onboarding record for a store.

        This is called automatically when a store is created during signup.
        """
        # Check if already exists
        existing = self.get_onboarding(store_id)
        if existing:
            logger.warning(f"Onboarding already exists for store {store_id}")
            return existing

        onboarding = StoreOnboarding(
            store_id=store_id,
            status=OnboardingStatus.NOT_STARTED.value,
            current_step=OnboardingStep.MERCHANT_PROFILE.value,
        )

        self.db.add(onboarding)
        self.db.flush()

        logger.info(f"Created onboarding record for store {store_id}")
        return onboarding

    def get_or_create_onboarding(self, store_id: int) -> StoreOnboarding:
        """Get existing onboarding or create new one."""
        onboarding = self.get_onboarding(store_id)
        if onboarding is None:
            onboarding = self.create_onboarding(store_id)
        return onboarding

    # =========================================================================
    # Status Helpers
    # =========================================================================

    def is_completed(self, store_id: int) -> bool:
        """Check if onboarding is completed for a store."""
        onboarding = self.get_onboarding(store_id)
        if onboarding is None:
            return False
        return onboarding.is_completed

    def get_status_response(self, store_id: int) -> dict:
        """
        Get full onboarding status for API response.

        Returns a dictionary with all step statuses and progress information.
        """
        onboarding = self.get_or_create_onboarding(store_id)

        return {
            "id": onboarding.id,
            "store_id": onboarding.store_id,
            "status": onboarding.status,
            "current_step": onboarding.current_step,
            # Step statuses
            "merchant_profile": {
                "completed": onboarding.step_merchant_profile_completed,
                "completed_at": onboarding.step_merchant_profile_completed_at,
                "data": onboarding.step_merchant_profile_data,
            },
            "letzshop_api": {
                "completed": onboarding.step_letzshop_api_completed,
                "completed_at": onboarding.step_letzshop_api_completed_at,
                "connection_verified": onboarding.step_letzshop_api_connection_verified,
            },
            "product_import": {
                "completed": onboarding.step_product_import_completed,
                "completed_at": onboarding.step_product_import_completed_at,
                "csv_url_set": onboarding.step_product_import_csv_url_set,
            },
            "order_sync": {
                "completed": onboarding.step_order_sync_completed,
                "completed_at": onboarding.step_order_sync_completed_at,
                "job_id": onboarding.step_order_sync_job_id,
            },
            # Progress tracking
            "completion_percentage": onboarding.completion_percentage,
            "completed_steps_count": onboarding.completed_steps_count,
            "total_steps": 4,
            # Completion info
            "is_completed": onboarding.is_completed,
            "started_at": onboarding.started_at,
            "completed_at": onboarding.completed_at,
            # Admin override info
            "skipped_by_admin": onboarding.skipped_by_admin,
            "skipped_at": onboarding.skipped_at,
            "skipped_reason": onboarding.skipped_reason,
        }

    # =========================================================================
    # Step 1: Merchant Profile
    # =========================================================================

    def get_merchant_profile_data(self, store_id: int) -> dict:
        """Get current merchant profile data for editing."""
        store = self._get_store(store_id)
        if not store:
            return {}

        merchant = store.merchant

        return {
            "merchant_name": merchant.name if merchant else None,
            "brand_name": store.name,
            "description": store.description,
            "contact_email": store.effective_contact_email,
            "contact_phone": store.effective_contact_phone,
            "website": store.effective_website,
            "business_address": store.effective_business_address,
            "tax_number": store.effective_tax_number,
            "default_language": store.default_language,
            "dashboard_language": store.dashboard_language,
        }

    def complete_merchant_profile(
        self,
        store_id: int,
        merchant_name: str | None = None,
        brand_name: str | None = None,
        description: str | None = None,
        contact_email: str | None = None,
        contact_phone: str | None = None,
        website: str | None = None,
        business_address: str | None = None,
        tax_number: str | None = None,
        default_language: str = "fr",
        dashboard_language: str = "fr",
    ) -> dict:
        """
        Save merchant profile and mark Step 1 as complete.

        Returns response with next step information.
        """
        # Check store exists BEFORE creating onboarding record (FK constraint)
        store = self._get_store(store_id)
        if not store:
            raise StoreNotFoundException(store_id)

        onboarding = self.get_or_create_onboarding(store_id)

        # Update onboarding status if this is the first step
        if onboarding.status == OnboardingStatus.NOT_STARTED.value:
            onboarding.status = OnboardingStatus.IN_PROGRESS.value
            onboarding.started_at = datetime.now(UTC)

        merchant = store.merchant

        # Update merchant name if provided
        if merchant and merchant_name:
            merchant.name = merchant_name

        # Update store fields
        if brand_name:
            store.name = brand_name
        if description is not None:
            store.description = description

        # Update contact info (store-level overrides)
        store.contact_email = contact_email
        store.contact_phone = contact_phone
        store.website = website
        store.business_address = business_address
        store.tax_number = tax_number

        # Update language settings
        store.default_language = default_language
        store.dashboard_language = dashboard_language

        # Store profile data in onboarding record
        onboarding.step_merchant_profile_data = {
            "merchant_name": merchant_name,
            "brand_name": brand_name,
            "description": description,
            "contact_email": contact_email,
            "contact_phone": contact_phone,
            "website": website,
            "business_address": business_address,
            "tax_number": tax_number,
            "default_language": default_language,
            "dashboard_language": dashboard_language,
        }

        # Mark step complete
        onboarding.mark_step_complete(OnboardingStep.MERCHANT_PROFILE.value)

        self.db.flush()

        logger.info(f"Completed merchant profile step for store {store_id}")

        return {
            "success": True,
            "step_completed": True,
            "next_step": onboarding.current_step,
            "message": "Merchant profile saved successfully",
        }

    # =========================================================================
    # Step 2: Letzshop API Configuration
    # =========================================================================

    def test_letzshop_api(
        self,
        api_key: str,
        shop_slug: str,
    ) -> dict:
        """
        Test Letzshop API connection without saving credentials.

        Returns connection test result with store info if successful.
        """
        credentials_service = LetzshopCredentialsService(self.db)

        # Test the API key
        success, response_time, error = credentials_service.test_api_key(api_key)

        if success:
            return {
                "success": True,
                "message": f"Connection successful ({response_time:.0f}ms)",
                "store_name": None,  # Would need to query Letzshop for this
                "store_id": None,
                "shop_slug": shop_slug,
            }
        return {
            "success": False,
            "message": error or "Connection failed",
            "store_name": None,
            "store_id": None,
            "shop_slug": None,
        }

    def complete_letzshop_api(
        self,
        store_id: int,
        api_key: str,
        shop_slug: str,
        letzshop_store_id: str | None = None,
    ) -> dict:
        """
        Save Letzshop API credentials and mark Step 2 as complete.

        Tests connection first, only saves if successful.
        """
        onboarding = self.get_or_create_onboarding(store_id)

        # Verify step order
        if not onboarding.can_proceed_to_step(OnboardingStep.LETZSHOP_API.value):
            raise OnboardingStepOrderException(
                current_step=onboarding.current_step,
                required_step=OnboardingStep.MERCHANT_PROFILE.value,
            )

        # Test connection first
        credentials_service = LetzshopCredentialsService(self.db)
        success, response_time, error = credentials_service.test_api_key(api_key)

        if not success:
            return {
                "success": False,
                "step_completed": False,
                "next_step": None,
                "message": f"Connection test failed: {error}",
                "connection_verified": False,
            }

        # Save credentials
        credentials_service.upsert_credentials(
            store_id=store_id,
            api_key=api_key,
            auto_sync_enabled=False,  # Enable after onboarding
            sync_interval_minutes=15,
        )

        # Update store with Letzshop identity
        store = self._get_store(store_id)
        if store:
            store.letzshop_store_slug = shop_slug
            if letzshop_store_id:
                store.letzshop_store_id = letzshop_store_id

        # Mark step complete
        onboarding.step_letzshop_api_connection_verified = True
        onboarding.mark_step_complete(OnboardingStep.LETZSHOP_API.value)

        self.db.flush()

        logger.info(f"Completed Letzshop API step for store {store_id}")

        return {
            "success": True,
            "step_completed": True,
            "next_step": onboarding.current_step,
            "message": "Letzshop API configured successfully",
            "connection_verified": True,
        }

    # =========================================================================
    # Step 3: Product & Order Import Configuration
    # =========================================================================

    def get_product_import_config(self, store_id: int) -> dict:
        """Get current product import configuration."""
        store = self._get_store(store_id)
        if not store:
            return {}

        return {
            "csv_url_fr": store.letzshop_csv_url_fr,
            "csv_url_en": store.letzshop_csv_url_en,
            "csv_url_de": store.letzshop_csv_url_de,
            "default_tax_rate": store.letzshop_default_tax_rate,
            "delivery_method": store.letzshop_delivery_method,
            "preorder_days": store.letzshop_preorder_days,
        }

    def complete_product_import(
        self,
        store_id: int,
        csv_url_fr: str | None = None,
        csv_url_en: str | None = None,
        csv_url_de: str | None = None,
        default_tax_rate: int = 17,
        delivery_method: str = "package_delivery",
        preorder_days: int = 1,
    ) -> dict:
        """
        Save product import configuration and mark Step 3 as complete.

        At least one CSV URL must be provided.
        """
        onboarding = self.get_or_create_onboarding(store_id)

        # Verify step order
        if not onboarding.can_proceed_to_step(OnboardingStep.PRODUCT_IMPORT.value):
            raise OnboardingStepOrderException(
                current_step=onboarding.current_step,
                required_step=OnboardingStep.LETZSHOP_API.value,
            )

        # Validate at least one CSV URL
        csv_urls_count = sum([
            bool(csv_url_fr),
            bool(csv_url_en),
            bool(csv_url_de),
        ])

        if csv_urls_count == 0:
            raise OnboardingCsvUrlRequiredException()

        # Update store settings
        store = self._get_store(store_id)
        if not store:
            raise StoreNotFoundException(store_id)

        store.letzshop_csv_url_fr = csv_url_fr
        store.letzshop_csv_url_en = csv_url_en
        store.letzshop_csv_url_de = csv_url_de
        store.letzshop_default_tax_rate = default_tax_rate
        store.letzshop_delivery_method = delivery_method
        store.letzshop_preorder_days = preorder_days

        # Mark step complete
        onboarding.step_product_import_csv_url_set = True
        onboarding.mark_step_complete(OnboardingStep.PRODUCT_IMPORT.value)

        self.db.flush()

        logger.info(f"Completed product import step for store {store_id}")

        return {
            "success": True,
            "step_completed": True,
            "next_step": onboarding.current_step,
            "message": "Product import configured successfully",
            "csv_urls_configured": csv_urls_count,
        }

    # =========================================================================
    # Step 4: Order Sync
    # =========================================================================

    def trigger_order_sync(
        self,
        store_id: int,
        user_id: int,
        days_back: int = 90,
        include_products: bool = True,
    ) -> dict:
        """
        Trigger historical order import and return job info.

        Creates a background job that imports historical orders from Letzshop.
        """
        onboarding = self.get_or_create_onboarding(store_id)

        # Verify step order
        if not onboarding.can_proceed_to_step(OnboardingStep.ORDER_SYNC.value):
            raise OnboardingStepOrderException(
                current_step=onboarding.current_step,
                required_step=OnboardingStep.PRODUCT_IMPORT.value,
            )

        # Create historical import job
        order_service = LetzshopOrderService(self.db)

        # Check for existing running job
        existing_job = order_service.get_running_historical_import_job(store_id)
        if existing_job:
            return {
                "success": True,
                "message": "Import job already running",
                "job_id": existing_job.id,
                "estimated_duration_minutes": 5,  # Estimate
            }

        # Create new job
        job = order_service.create_historical_import_job(
            store_id=store_id,
            user_id=user_id,
        )

        # Store job ID in onboarding
        onboarding.step_order_sync_job_id = job.id

        self.db.flush()

        logger.info(f"Triggered order sync job {job.id} for store {store_id}")

        return {
            "success": True,
            "message": "Historical import started",
            "job_id": job.id,
            "estimated_duration_minutes": 5,  # Estimate
        }

    def get_order_sync_progress(
        self,
        store_id: int,
        job_id: int,
    ) -> dict:
        """
        Get progress of historical import job.

        Returns current status, progress, and counts.
        """
        order_service = LetzshopOrderService(self.db)
        job = order_service.get_historical_import_job_by_id(store_id, job_id)

        if not job:
            return {
                "job_id": job_id,
                "status": "not_found",
                "progress_percentage": 0,
                "current_phase": None,
                "orders_imported": 0,
                "orders_total": None,
                "products_imported": 0,
                "started_at": None,
                "completed_at": None,
                "estimated_remaining_seconds": None,
                "error_message": "Job not found",
            }

        # Calculate progress percentage
        progress = 0
        if job.status == "completed":
            progress = 100
        elif job.status == "failed":
            progress = 0
        elif job.status == "processing":
            # Use orders_processed and shipments_fetched for progress
            total = job.shipments_fetched or 0
            processed = job.orders_processed or 0
            if total > 0:
                progress = int(processed / total * 100)
            else:
                progress = 50  # Indeterminate
        elif job.status == "fetching":
            # Show partial progress during fetch phase
            if job.total_pages and job.total_pages > 0:
                progress = int((job.current_page or 0) / job.total_pages * 50)
            else:
                progress = 25  # Indeterminate

        # Determine current phase
        current_phase = job.current_phase or job.status

        return {
            "job_id": job.id,
            "status": job.status,
            "progress_percentage": progress,
            "current_phase": current_phase,
            "orders_imported": job.orders_imported or 0,
            "orders_total": job.shipments_fetched,
            "products_imported": job.products_matched or 0,
            "started_at": job.started_at,
            "completed_at": job.completed_at,
            "estimated_remaining_seconds": None,  # TODO: Calculate
            "error_message": job.error_message,
        }

    def complete_order_sync(
        self,
        store_id: int,
        job_id: int,
    ) -> dict:
        """
        Mark order sync step as complete after job finishes.

        Also marks the entire onboarding as complete.
        """
        onboarding = self.get_or_create_onboarding(store_id)

        # Verify job is complete
        order_service = LetzshopOrderService(self.db)
        job = order_service.get_historical_import_job_by_id(store_id, job_id)

        if not job:
            raise OnboardingSyncJobNotFoundException(job_id)

        if job.status not in ("completed", "failed"):
            raise OnboardingSyncNotCompleteException(job.status)

        # Mark step complete (even if job failed - they can retry later)
        onboarding.mark_step_complete(OnboardingStep.ORDER_SYNC.value)

        # Enable auto-sync now that onboarding is complete
        credentials_service = LetzshopCredentialsService(self.db)
        credentials = credentials_service.get_credentials(store_id)
        if credentials:
            credentials.auto_sync_enabled = True

        self.db.flush()

        # Get store code for redirect URL
        store = self._get_store(store_id)
        store_code = store.store_code if store else ""

        logger.info(f"Completed onboarding for store {store_id}")

        return {
            "success": True,
            "step_completed": True,
            "onboarding_completed": True,
            "message": "Onboarding complete! Welcome to Orion.",
            "redirect_url": f"/store/{store_code}/dashboard",
        }

    # =========================================================================
    # Admin Skip
    # =========================================================================

    def skip_onboarding(
        self,
        store_id: int,
        admin_user_id: int,
        reason: str,
    ) -> dict:
        """
        Admin-only: Skip onboarding for a store.

        Used for support cases where manual setup is needed.
        """
        onboarding = self.get_or_create_onboarding(store_id)

        onboarding.skipped_by_admin = True
        onboarding.skipped_at = datetime.now(UTC)
        onboarding.skipped_reason = reason
        onboarding.skipped_by_user_id = admin_user_id
        onboarding.status = OnboardingStatus.SKIPPED.value

        self.db.flush()

        logger.info(
            f"Admin {admin_user_id} skipped onboarding for store {store_id}: {reason}"
        )

        return {
            "success": True,
            "message": "Onboarding skipped by admin",
            "store_id": store_id,
            "skipped_at": onboarding.skipped_at,
        }


# Singleton-style convenience instance
def get_onboarding_service(db: Session) -> OnboardingService:
    """Get an OnboardingService instance."""
    return OnboardingService(db)
