"""
Service Contract Migration Wizard
==================================

Wizard for migrating vTiger service contracts to Hub archive system.

Workflow:
1. User selects service contract from vTiger
2. Load contract's cases + timelogs
3. For each case: archive to tticket_archived_tickets
4. For each timelog: transfer hours to customer's klippekort (top-up)
5. Wizard displays progress with dry-run support

Dry-Run Mode:
- All operations logged but not committed
- Database transactions rolled back
- UI shows what would happen
"""

import logging
import json
import re
from datetime import datetime
from decimal import Decimal
from typing import List, Dict, Optional, Any, Tuple

from app.core.database import execute_query, execute_insert, execute_update, execute_query_single
from app.core.config import settings
from app.services.vtiger_service import get_vtiger_service
from app.ticket.backend.klippekort_service import KlippekortService

logger = logging.getLogger(__name__)


class ServiceContractWizardService:
    """Service for vTiger service contract migration wizard"""
    
    @staticmethod
    async def get_active_contracts() -> List[Dict[str, Any]]:
        """
        Fetch list of active service contracts from vTiger
        
        Returns:
            List of contracts with id, contract_number, subject, account_id, etc.
        """
        try:
            vtiger_service = get_vtiger_service()
            contracts = await vtiger_service.get_service_contracts()
            
            logger.info(f"✅ Fetched {len(contracts)} active service contracts from vTiger")
            return contracts
            
        except Exception as e:
            logger.error(f"❌ Error fetching service contracts: {e}")
            return []
    
    @staticmethod
    async def load_contract_detailed_data(
        contract_id: str,
        account_id: str = ""
    ) -> Dict[str, Any]:
        """
        Load all data for a service contract: cases + timelogs
        
        Args:
            contract_id: vTiger service contract ID (e.g., "75x123") - required
            account_id: vTiger account ID (e.g., "3x760") - optional, will try to extract from contract
            
        Returns:
            Dict with contract info + cases + timelogs
        """
        try:
            vtiger_service = get_vtiger_service()
            
            # Fetch contract details (without account filter if account_id not provided)
            contracts = await vtiger_service.get_service_contracts(account_id if account_id else None)
            contract = next((c for c in contracts if c.get('id') == contract_id), None)
            
            if not contract:
                logger.error(f"❌ Service contract {contract_id} not found")
                return {}
            
            # Extract account_id from contract if not provided
            if not account_id:
                account_id = (
                    contract.get('account_id')
                    or contract.get('accountid')
                    or contract.get('cf_service_contracts_account')
                    or contract.get('sc_related_to')
                    or ""
                )
                if not account_id:
                    logger.error(f"❌ Could not determine account_id for contract {contract_id}")
                    return {}
                logger.info(f"ℹ️ Extracted account_id={account_id} from contract")
            
            # Fetch cases linked to contract
            cases = await vtiger_service.get_service_contract_cases(contract_id)
            logger.info(f"✅ Found {len(cases)} cases for contract {contract_id}")
            
            # Fetch timelogs linked to contract
            timelogs = await vtiger_service.get_service_contract_timelogs(contract_id)
            logger.info(f"✅ Found {len(timelogs)} timelogs for contract {contract_id}")
            
            # Get account info for klippekort lookup
            account = await vtiger_service.get_account(account_id)
            
            # Map vTiger account to Hub customer
            customer_id = ServiceContractWizardService._lookup_customer_id(account_id)
            
            if not customer_id:
                logger.warning(f"⚠️ No Hub customer found for vTiger account {account_id}")
            
            # Get available klippekort for customer
            available_cards = KlippekortService.get_active_cards_for_customer(customer_id) if customer_id else []
            
            return {
                'contract_id': contract_id,
                'contract_number': contract.get('contract_number') or contract.get('contract_no', ''),
                'subject': contract.get('subject', ''),
                'account_id': account_id,
                'account_name': account.get('accountname', '') if account else '',
                'customer_id': customer_id,
                'cases': cases,
                'timelogs': timelogs,
                'available_cards': available_cards,
                'total_items': len(cases) + len(timelogs),
            }
            
        except Exception as e:
            logger.error(f"❌ Error loading contract data: {e}")
            return {}
    
    @staticmethod
    def _lookup_customer_id(vtiger_account_id: str) -> Optional[int]:
        """
        Map vTiger account ID to Hub customer ID via tmodule_customers table
        
        Args:
            vtiger_account_id: vTiger account ID (e.g., "3x760")
            
        Returns:
            Hub customer ID or None
        """
        try:
            result = execute_query_single(
                "SELECT id FROM tmodule_customers WHERE vtiger_id = %s LIMIT 1",
                (vtiger_account_id,)
            )
            return result['id'] if result else None
            
        except Exception as e:
            logger.error(f"❌ Error looking up customer ID: {e}")
            return None
    
    @staticmethod
    def archive_case(
        case_data: Dict[str, Any],
        contract_id: str,
        dry_run: bool = False
    ) -> Tuple[bool, str, Optional[int]]:
        """
        Archive a single case to tticket_archived_tickets
        
        Args:
            case_data: Case dict from vTiger (id, title, description, etc.)
            contract_id: Service contract ID (for reference)
            dry_run: If True, log only without commit
            
        Returns:
            (success: bool, message: str, archived_id: int or None)
        """
        try:
            case_id = case_data.get('id')
            title = case_data.get('title', case_data.get('subject', 'Untitled'))
            description = case_data.get('description', '')
            status = case_data.get('ticketstatus', case_data.get('status', 'Open'))
            priority = case_data.get('priority', 'Normal')
            
            if dry_run:
                logger.info(f"🔍 DRY RUN: Would archive case {case_id}: '{title}'")
                return (True, f"[DRY RUN] Would archive: {title}", None)
            
            # Archive to tticket_archived_tickets
            archived_id = execute_insert(
                """
                INSERT INTO tticket_archived_tickets (
                    source_system, external_id, ticket_number, title,
                    description, status, priority, source_created_at, raw_data
                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
                """,
                (
                    'vtiger_service_contract',
                    case_id,
                    f"SC:{contract_id}",  # Reference to service contract
                    title,
                    description,
                    status,
                    priority,
                    datetime.now(),
                    json.dumps(case_data)
                )
            )
            
            logger.info(f"✅ Archived case {case_id} to tticket_archived_tickets (ID: {archived_id})")
            return (True, f"Archived: {title}", archived_id)
            
        except Exception as e:
            logger.error(f"❌ Error archiving case: {e}")
            return (False, f"Error: {str(e)}", None)
    
    @staticmethod
    def _extract_timelog_hours(timelog_data: Dict[str, Any]) -> Tuple[Decimal, Decimal]:
        """
        Extract and normalize timelog hours from vTiger payload.

        Returns:
            (normalized_hours, raw_hours)
        """
        raw_value = None
        field_used = None

        for key in ("time_spent", "duration", "total_hours", "hours"):
            value = timelog_data.get(key)
            if value not in (None, ""):
                raw_value = value
                field_used = key
                break

        # vTiger's 'duration' field is in seconds, convert to minutes
        if field_used == "duration" and raw_value:
            try:
                seconds = Decimal(str(raw_value))
                raw_minutes = seconds / Decimal(60)
            except Exception:
                raw_minutes = ServiceContractWizardService._parse_timelog_minutes(raw_value)
        else:
            raw_minutes = ServiceContractWizardService._parse_timelog_minutes(raw_value)

        normalized = raw_minutes / Decimal(60)

        return normalized, raw_minutes

    @staticmethod
    def _parse_timelog_minutes(raw_value: Any) -> Decimal:
        """Parse vTiger timelog time spent into minutes."""
        if raw_value in (None, ""):
            return Decimal(0)

        if isinstance(raw_value, Decimal):
            return raw_value

        if isinstance(raw_value, (int, float)):
            return Decimal(str(raw_value))

        raw_str = str(raw_value).strip().lower()
        if not raw_str:
            return Decimal(0)

        time_match = re.match(r"^(\d+):(\d+)(?::(\d+))?$", raw_str)
        if time_match:
            hours = int(time_match.group(1))
            minutes = int(time_match.group(2))
            seconds = int(time_match.group(3) or 0)
            return Decimal(hours * 60 + minutes) + (Decimal(seconds) / Decimal(60))

        total_minutes = Decimal(0)
        has_unit = False

        unit_patterns = [
            (r"(\d+(?:[\.,]\d+)?)\s*(?:h|hour|hours)\b", Decimal(60)),
            (r"(\d+(?:[\.,]\d+)?)\s*(?:m|min|minute|minutes)\b", Decimal(1)),
            (r"(\d+(?:[\.,]\d+)?)\s*(?:s|sec|second|seconds)\b", Decimal(1) / Decimal(60)),
        ]

        for pattern, multiplier in unit_patterns:
            match = re.search(pattern, raw_str)
            if match:
                has_unit = True
                value = Decimal(match.group(1).replace(",", "."))
                total_minutes += value * multiplier

        if has_unit:
            return total_minutes

        try:
            return Decimal(raw_str.replace(",", "."))
        except Exception:
            return Decimal(0)

    @staticmethod
    def _apply_rounding_for_card(card_id: int, hours: Decimal) -> Decimal:
        """
        Apply rounding rules for a prepaid card or fallback to timetracking settings.
        """
        from decimal import ROUND_CEILING, ROUND_DOWN, ROUND_HALF_UP

        card = execute_query_single(
            "SELECT rounding_minutes FROM tticket_prepaid_cards WHERE id = %s",
            (card_id,)
        )

        rounding_minutes = int(card.get('rounding_minutes') or 0) if card else 0
        if rounding_minutes > 0:
            interval = Decimal(rounding_minutes) / Decimal(60)
            return (hours / interval).to_integral_value(rounding=ROUND_CEILING) * interval

        if settings.TIMETRACKING_AUTO_ROUND:
            increment = Decimal(str(settings.TIMETRACKING_ROUND_INCREMENT))
            method = settings.TIMETRACKING_ROUND_METHOD

            if method == "down":
                return (hours / increment).to_integral_value(rounding=ROUND_DOWN) * increment
            if method == "nearest":
                return (hours / increment).to_integral_value(rounding=ROUND_HALF_UP) * increment
            return (hours / increment).to_integral_value(rounding=ROUND_CEILING) * increment

        return hours

    @staticmethod
    def transfer_timelog_to_klippekort(
        timelog_data: Dict[str, Any],
        card_id: int,
        customer_id: int,
        contract_id: str,
        dry_run: bool = False
    ) -> Tuple[bool, str, Optional[Dict]]:
        """
        Transfer timelog hours to customer's klippekort via top-up
        
        Args:
            timelog_data: Timelog dict from vTiger (id, hours, description, etc.)
            card_id: Klippekort card ID to transfer to
            customer_id: Hub customer ID
            contract_id: Service contract ID (for reference)
            dry_run: If True, calculate but don't commit
            
        Returns:
            (success: bool, message: str, transaction_result: dict or None)
        """
        try:
            timelog_id = timelog_data.get('id')
            hours, raw_minutes = ServiceContractWizardService._extract_timelog_hours(timelog_data)
            description = timelog_data.get('description', '')
            work_date = timelog_data.get('workdate', '')

            if raw_minutes > 0:
                logger.info(
                    f"ℹ️ Normalized timelog {timelog_id} minutes {raw_minutes} to hours {hours}"
                )
            
            if hours <= 0:
                logger.warning(f"⚠️ Skipping timelog {timelog_id} with {hours} hours")
                return (False, f"Skipped: 0 hours", None)
            
            # Verify card exists and belongs to customer
            card = KlippekortService.get_card(card_id)
            if not card or card['customer_id'] != customer_id:
                msg = f"Card {card_id} not found or doesn't belong to customer {customer_id}"
                logger.error(f"❌ {msg}")
                return (False, msg, None)
            
            rounded_hours = ServiceContractWizardService._apply_rounding_for_card(card_id, hours)

            if dry_run:
                logger.info(
                    f"🔍 DRY RUN: Would transfer {rounded_hours}h to card {card_id} from timelog {timelog_id}"
                )
                return (True, f"[DRY RUN] Would transfer {rounded_hours}h to card", None)
            
            # Top-up klippekort with timelog hours
            transaction = KlippekortService.top_up_card(
                card_id,
                rounded_hours,
                user_id=None,
                note=f"SC migration: {description} (vTiger {timelog_id}) from {work_date}"
            )
            
            logger.info(f"✅ Transferred {rounded_hours}h from timelog {timelog_id} to card {card_id}")
            return (True, f"Transferred {rounded_hours}h to card {card.get('card_number', '')}", transaction)
            
        except Exception as e:
            logger.error(f"❌ Error transferring timelog: {e}")
            return (False, f"Error: {str(e)}", None)
    
    @staticmethod
    def get_wizard_summary(
        contract_data: Dict[str, Any],
        actions: List[Dict[str, Any]],
        dry_run: bool = False
    ) -> Dict[str, Any]:
        """
        Generate summary report of wizard actions
        
        Args:
            contract_data: Contract info (contract_id, subject, etc.)
            actions: List of action dicts from wizard steps
            dry_run: Whether wizard ran in dry-run mode
            
        Returns:
            Summary dict with counts and status
        """
        archived_count = sum(1 for a in actions if a.get('type') == 'archive' and a.get('success'))
        transferred_count = sum(1 for a in actions if a.get('type') == 'transfer' and a.get('success'))
        failed_count = sum(1 for a in actions if not a.get('success'))
        
        return {
            'contract_id': contract_data.get('contract_id', ''),
            'contract_number': contract_data.get('contract_number', ''),
            'subject': contract_data.get('subject', ''),
            'dry_run': dry_run,
            'total_items_processed': len(actions),
            'cases_archived': archived_count,
            'timelogs_transferred': transferred_count,
            'failed_items': failed_count,
            'status': 'completed_with_errors' if failed_count > 0 else 'completed',
            'timestamp': datetime.now().isoformat(),
        }
