"""
Simply-CRM Integration Service
Sync abonnementer, fakturaer og kunder fra Simply-CRM (VTiger fork)

Simply-CRM bruger webservice.php endpoint med challenge-token authentication:
- Endpoint: /webservice.php
- Auth: getchallenge + login med MD5 hash
- Moduler: Accounts, Invoice, Products, SalesOrder
"""
import logging
import json
import hashlib
import aiohttp
from typing import List, Dict, Optional, Any
from datetime import datetime, date
from app.core.config import settings

logger = logging.getLogger(__name__)


class SimplyCRMService:
    """Service for integrating with Simply-CRM via webservice.php (VTiger fork)"""
    
    def __init__(self):
        # Try SIMPLYCRM_* first, fallback to OLD_VTIGER_* for backward compatibility
        self.base_url = getattr(settings, 'SIMPLYCRM_URL', None) or getattr(settings, 'OLD_VTIGER_URL', None)
        self.username = getattr(settings, 'SIMPLYCRM_USERNAME', None) or getattr(settings, 'OLD_VTIGER_USERNAME', None)
        self.access_key = getattr(settings, 'SIMPLYCRM_API_KEY', None) or getattr(settings, 'OLD_VTIGER_API_KEY', None)
        
        self.session_name: Optional[str] = None
        self.session: Optional[aiohttp.ClientSession] = None
        self.last_query_error: Optional[Dict[str, Any]] = None
        self._denied_relation_fields: set[str] = set()
        
        if not all([self.base_url, self.username, self.access_key]):
            logger.warning("⚠️ Simply-CRM credentials not configured (SIMPLYCRM_* or OLD_VTIGER_* settings)")
    
    async def __aenter__(self):
        """Context manager entry - create session and login"""
        self.session = aiohttp.ClientSession()
        await self.login()
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """Context manager exit - close session"""
        if self.session:
            await self.session.close()
            self.session = None
            self.session_name = None
    
    async def login(self) -> bool:
        """
        Login to Simply-CRM using challenge-token authentication
        
        Returns:
            True if login successful
        """
        if not self.base_url or not self.username or not self.access_key:
            logger.error("❌ Simply-CRM credentials not configured")
            return False
        
        try:
            if not self.session:
                self.session = aiohttp.ClientSession()
            
            # Step 1: Get challenge token
            async with self.session.get(
                f"{self.base_url}/webservice.php",
                params={"operation": "getchallenge", "username": self.username},
                timeout=aiohttp.ClientTimeout(total=30)
            ) as response:
                if not response.ok:
                    logger.error(f"❌ Simply-CRM challenge request failed: {response.status}")
                    return False
                
                data = await response.json()
                if not data.get("success"):
                    logger.error(f"❌ Simply-CRM challenge failed: {data}")
                    return False
                
                token = data["result"]["token"]
            
            # Step 2: Generate access key hash
            access_key_hash = hashlib.md5(f"{token}{self.access_key}".encode()).hexdigest()
            
            # Step 3: Login
            async with self.session.post(
                f"{self.base_url}/webservice.php",
                data={
                    "operation": "login",
                    "username": self.username,
                    "accessKey": access_key_hash
                },
                timeout=aiohttp.ClientTimeout(total=30)
            ) as response:
                if not response.ok:
                    logger.error(f"❌ Simply-CRM login request failed: {response.status}")
                    return False
                
                data = await response.json()
                if not data.get("success"):
                    logger.error(f"❌ Simply-CRM login failed: {data}")
                    return False
                
                self.session_name = data["result"]["sessionName"]
                session_preview = self.session_name[:20] if self.session_name else "unknown"
                logger.info(f"✅ Simply-CRM login successful (session: {session_preview}...)")
                return True
        
        except aiohttp.ClientError as e:
            logger.error(f"❌ Simply-CRM connection error: {e}")
            return False
        except Exception as e:
            logger.error(f"❌ Simply-CRM login error: {e}")
            return False
    
    async def test_connection(self) -> bool:
        """
        Test Simply-CRM connection by logging in
        
        Returns:
            True if connection successful
        """
        try:
            async with aiohttp.ClientSession() as session:
                self.session = session
                result = await self.login()
                self.session = None
                return result
        except Exception as e:
            logger.error(f"❌ Simply-CRM connection test failed: {e}")
            return False
    
    async def _ensure_session(self):
        """Ensure we have an active session"""
        if not self.session:
            self.session = aiohttp.ClientSession()
        if not self.session_name:
            await self.login()
    
    async def query(self, query_string: str) -> List[Dict]:
        """
        Execute a query on Simply-CRM
        
        Args:
            query_string: SQL-like query (e.g., "SELECT * FROM Accounts LIMIT 100;")
            
        Returns:
            List of records
        """
        await self._ensure_session()
        
        if not self.session_name or not self.session:
            logger.error("❌ Not logged in to Simply-CRM")
            return []
        
        try:
            async with self.session.get(
                f"{self.base_url}/webservice.php",
                params={
                    "operation": "query",
                    "sessionName": self.session_name,
                    "query": query_string
                },
                timeout=aiohttp.ClientTimeout(total=60)
            ) as response:
                if not response.ok:
                    logger.error(f"❌ Simply-CRM query failed: {response.status}")
                    return []
                
                data = await response.json()
                if not data.get("success"):
                    error = data.get("error", {})
                    self.last_query_error = error if isinstance(error, dict) else {"message": str(error)}
                    if (self.last_query_error or {}).get("code") == "ACCESS_DENIED":
                        logger.warning(f"⚠️ Simply-CRM query access denied: {error}")
                    else:
                        logger.error(f"❌ Simply-CRM query error: {error}")
                    return []
                
                result = data.get("result", [])
                self.last_query_error = None
                logger.debug(f"✅ Simply-CRM query returned {len(result)} records")
                return result
                
        except Exception as e:
            self.last_query_error = {"message": str(e)}
            logger.error(f"❌ Simply-CRM query error: {e}")
            return []

    # =========================================================================
    # TICKETS (ARCHIVED IMPORT)
    # =========================================================================

    async def fetch_tickets(self, limit: int = 5000) -> List[Dict]:
        """
        Fetch tickets from Simply-CRM (module configurable in settings).

        Args:
            limit: Maximum number of tickets to return

        Returns:
            List of ticket records
        """
        module_name = getattr(settings, "SIMPLYCRM_TICKET_MODULE", "Tickets")
        all_records: List[Dict] = []
        offset = 0
        batch_size = 200

        while len(all_records) < limit:
            query = f"SELECT * FROM {module_name} LIMIT {offset}, {batch_size};"
            batch = await self.query(query)
            if not batch:
                break
            all_records.extend(batch)
            offset += batch_size

            if len(batch) < batch_size:
                break

        return all_records[:limit]

    async def fetch_ticket_comments(self, ticket_id: str) -> List[Dict]:
        """
        Fetch comments for a ticket from Simply-CRM.

        Args:
            ticket_id: Record ID for the ticket

        Returns:
            List of comment records
        """
        module_name = getattr(settings, "SIMPLYCRM_TICKET_COMMENT_MODULE", "ModComments")
        relation_field = getattr(settings, "SIMPLYCRM_TICKET_COMMENT_RELATION_FIELD", "related_to")
        relation_candidates: List[str] = []
        for candidate in [relation_field, "parent_id", "ticket_id", "crmid", "related_to"]:
            if candidate and candidate not in relation_candidates:
                relation_candidates.append(candidate)

        for candidate in relation_candidates:
            if candidate in self._denied_relation_fields:
                continue

            query = f"SELECT * FROM {module_name} WHERE {candidate} = '{ticket_id}' ORDER BY createdtime ASC;"
            result = await self.query(query)
            error_code = (self.last_query_error or {}).get("code")

            if error_code == "ACCESS_DENIED":
                self._denied_relation_fields.add(candidate)
                continue

            return result

        return []

    async def fetch_ticket_emails(self, ticket_id: str) -> List[Dict]:
        """
        Fetch email records for a ticket from Simply-CRM.

        Args:
            ticket_id: Record ID for the ticket

        Returns:
            List of email records
        """
        module_name = getattr(settings, "SIMPLYCRM_TICKET_EMAIL_MODULE", "Emails")
        relation_field = getattr(settings, "SIMPLYCRM_TICKET_EMAIL_RELATION_FIELD", "parent_id")
        fallback_relation_field = getattr(settings, "SIMPLYCRM_TICKET_EMAIL_FALLBACK_RELATION_FIELD", "related_to")

        records: List[Dict] = []
        relation_candidates: List[str] = []
        for candidate in [relation_field, fallback_relation_field, "parent_id", "ticket_id", "crmid"]:
            if candidate and candidate not in relation_candidates:
                relation_candidates.append(candidate)

        for candidate in relation_candidates:
            if candidate in self._denied_relation_fields:
                continue

            query = f"SELECT * FROM {module_name} WHERE {candidate} = '{ticket_id}';"
            batch = await self.query(query)
            error_code = (self.last_query_error or {}).get("code")

            if error_code == "ACCESS_DENIED":
                self._denied_relation_fields.add(candidate)
                continue

            records.extend(batch)
            if records:
                break

        # De-duplicate by record id if present
        seen_ids = set()
        unique_records: List[Dict] = []
        for record in records:
            record_id = record.get("id")
            if record_id and record_id in seen_ids:
                continue
            if record_id:
                seen_ids.add(record_id)
            unique_records.append(record)

        return unique_records
    
    async def retrieve(self, record_id: str) -> Optional[Dict]:
        """
        Retrieve a specific record by ID
        
        Args:
            record_id: VTiger-style ID (e.g., "6x12345")
            
        Returns:
            Record dict or None
        """
        await self._ensure_session()
        
        if not self.session_name or not self.session:
            return None
        
        try:
            async with self.session.get(
                f"{self.base_url}/webservice.php",
                params={
                    "operation": "retrieve",
                    "sessionName": self.session_name,
                    "id": record_id
                },
                timeout=aiohttp.ClientTimeout(total=30)
            ) as response:
                if not response.ok:
                    return None
                
                data = await response.json()
                if data.get("success"):
                    return data.get("result")
                return None
        except Exception as e:
            logger.error(f"❌ Simply-CRM retrieve error: {e}")
            return None

    async def list_types(self) -> List[str]:
        """
        List available module types from Simply-CRM.
        """
        await self._ensure_session()

        if not self.session_name or not self.session:
            logger.error("❌ Not logged in to Simply-CRM")
            return []

        try:
            async with self.session.get(
                f"{self.base_url}/webservice.php",
                params={
                    "operation": "listtypes",
                    "sessionName": self.session_name
                },
                timeout=aiohttp.ClientTimeout(total=30)
            ) as response:
                if not response.ok:
                    logger.error(f"❌ Simply-CRM listtypes failed: {response.status}")
                    return []

                data = await response.json()
                if not data.get("success"):
                    logger.error(f"❌ Simply-CRM listtypes error: {data}")
                    return []

                result = data.get("result", {})
                return result.get("types", [])

        except Exception as e:
            logger.error(f"❌ Simply-CRM listtypes error: {e}")
            return []
    
    # =========================================================================
    # SUBSCRIPTIONS
    # =========================================================================
    
    async def fetch_subscriptions(self, limit: int = 100, offset: int = 0) -> List[Dict]:
        """
        Fetch subscriptions from Simply-CRM via recurring SalesOrders
        
        SalesOrders with enable_recurring='1' are the subscription source in Simply-CRM.
        """
        query = f"SELECT * FROM SalesOrder WHERE enable_recurring = '1' LIMIT {offset}, {limit};"
        return await self.query(query)
    
    async def fetch_active_subscriptions(self) -> List[Dict]:
        """
        Fetch all active recurring SalesOrders (subscriptions)
        
        Returns deduplicated list of unique SalesOrders.
        """
        all_records = []
        offset = 0
        limit = 100
        seen_ids = set()
        
        while True:
            query = f"SELECT * FROM SalesOrder WHERE enable_recurring = '1' LIMIT {offset}, {limit};"
            batch = await self.query(query)
            
            if not batch:
                break
            
            # Deduplicate by id (VTiger returns one row per line item)
            for record in batch:
                record_id = record.get('id')
                if record_id and record_id not in seen_ids:
                    seen_ids.add(record_id)
                    all_records.append(record)
            
            if len(batch) < limit:
                break
                
            offset += limit
        
        logger.info(f"📊 Fetched {len(all_records)} unique recurring SalesOrders from Simply-CRM")
        return all_records
    
    # =========================================================================
    # INVOICES
    # =========================================================================
    
    async def fetch_invoices(self, limit: int = 100, offset: int = 0) -> List[Dict]:
        """Fetch invoices from Simply-CRM"""
        query = f"SELECT * FROM Invoice LIMIT {offset}, {limit};"
        return await self.query(query)
    
    async def fetch_invoices_by_account(self, account_id: str) -> List[Dict]:
        """Fetch all invoices for a specific account"""
        query = f"SELECT * FROM Invoice WHERE account_id = '{account_id}';"
        return await self.query(query)
    
    async def fetch_invoice_with_lines(self, invoice_id: str) -> Optional[Dict]:
        """Fetch invoice with full line item details"""
        return await self.retrieve(invoice_id)
    
    async def fetch_recent_invoices(self, days: int = 30) -> List[Dict]:
        """Fetch invoices from the last N days"""
        from datetime import datetime, timedelta
        since_date = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
        
        all_invoices = []
        offset = 0
        limit = 100
        
        while True:
            query = f"SELECT * FROM Invoice WHERE invoicedate >= '{since_date}' LIMIT {offset}, {limit};"
            batch = await self.query(query)
            
            if not batch:
                break
                
            all_invoices.extend(batch)
            
            if len(batch) < limit:
                break
                
            offset += limit
        
        logger.info(f"📊 Fetched {len(all_invoices)} invoices from last {days} days")
        return all_invoices
    
    # =========================================================================
    # ACCOUNTS (CUSTOMERS)
    # =========================================================================
    
    async def fetch_accounts(self, limit: int = 100, offset: int = 0) -> List[Dict]:
        """Fetch accounts (customers) from Simply-CRM"""
        query = f"SELECT * FROM Accounts LIMIT {offset}, {limit};"
        return await self.query(query)
    
    async def fetch_account_by_id(self, account_id: str) -> Optional[Dict]:
        """Fetch account by VTiger ID (e.g., '11x12345')"""
        return await self.retrieve(account_id)
    
    async def fetch_account_by_name(self, name: str) -> Optional[Dict]:
        """Find account by name"""
        query = f"SELECT * FROM Accounts WHERE accountname = '{name}';"
        results = await self.query(query)
        return results[0] if results else None
    
    async def fetch_account_by_cvr(self, cvr: str) -> Optional[Dict]:
        """Find account by CVR number"""
        query = f"SELECT * FROM Accounts WHERE siccode = '{cvr}';"
        results = await self.query(query)
        
        if not results:
            # Try vat_number field
            query = f"SELECT * FROM Accounts WHERE vat_number = '{cvr}';"
            results = await self.query(query)
        
        return results[0] if results else None
    
    async def fetch_all_accounts(self) -> List[Dict]:
        """Fetch all accounts (with pagination)"""
        all_accounts = []
        offset = 0
        limit = 100
        
        while True:
            batch = await self.fetch_accounts(limit, offset)
            
            if not batch:
                break
                
            all_accounts.extend(batch)
            
            if len(batch) < limit:
                break
                
            offset += limit
        
        logger.info(f"📊 Fetched {len(all_accounts)} accounts from Simply-CRM")
        return all_accounts
    
    # =========================================================================
    # PRODUCTS
    # =========================================================================
    
    async def fetch_products(self, limit: int = 100, offset: int = 0) -> List[Dict]:
        """Fetch products from Simply-CRM"""
        query = f"SELECT * FROM Products LIMIT {offset}, {limit};"
        return await self.query(query)
    
    async def fetch_product_by_number(self, product_number: str) -> Optional[Dict]:
        """Find product by product number"""
        query = f"SELECT * FROM Products WHERE product_no = '{product_number}';"
        results = await self.query(query)
        return results[0] if results else None
    
    # =========================================================================
    # SYNC HELPERS
    # =========================================================================
    
    async def get_modified_since(self, module: str, since_date: str) -> List[Dict]:
        """Get records modified since a specific date"""
        all_records = []
        offset = 0
        limit = 100
        
        while True:
            query = f"SELECT * FROM {module} WHERE modifiedtime >= '{since_date}' LIMIT {offset}, {limit};"
            batch = await self.query(query)
            
            if not batch:
                break
                
            all_records.extend(batch)
            
            if len(batch) < limit:
                break
                
            offset += limit
        
        return all_records
    
    def extract_subscription_data(self, raw_salesorder: Dict) -> Dict:
        """
        Extract and normalize subscription data from Simply-CRM SalesOrder format
        
        SalesOrders with enable_recurring='1' are the subscription source.
        Key fields:
        - recurring_frequency: Monthly, Quarterly, Yearly
        - start_period / end_period: Subscription period
        - cf_abonnementsperiode_dato: Binding end date
        - arr: Annual Recurring Revenue
        - sostatus: Created, Approved, Lukket
        """
        return {
            'simplycrm_id': raw_salesorder.get('id'),
            'salesorder_no': raw_salesorder.get('salesorder_no'),
            'name': raw_salesorder.get('subject', ''),
            'account_id': raw_salesorder.get('account_id'),
            'status': self._map_salesorder_status(raw_salesorder.get('sostatus')),
            'start_date': raw_salesorder.get('start_period'),
            'end_date': raw_salesorder.get('end_period'),
            'binding_end_date': raw_salesorder.get('cf_abonnementsperiode_dato'),
            'billing_frequency': self._map_billing_frequency(raw_salesorder.get('recurring_frequency')),
            'auto_invoicing': raw_salesorder.get('auto_invoicing'),
            'last_recurring_date': raw_salesorder.get('last_recurring_date'),
            'total_amount': self._parse_amount(raw_salesorder.get('hdnGrandTotal')),
            'subtotal': self._parse_amount(raw_salesorder.get('hdnSubTotal')),
            'arr': self._parse_amount(raw_salesorder.get('arr')),
            'currency': 'DKK',
            'modified_time': raw_salesorder.get('modifiedtime'),
            'created_time': raw_salesorder.get('createdtime'),
            'eco_order_number': raw_salesorder.get('eco_order_number'),
        }
    
    def _map_salesorder_status(self, status: Optional[str]) -> str:
        """Map Simply-CRM SalesOrder status to OmniSync status"""
        if not status:
            return 'active'
        status_map = {
            'Created': 'active',
            'Approved': 'active',
            'Delivered': 'active',
            'Lukket': 'cancelled',
            'Cancelled': 'cancelled',
        }
        return status_map.get(status, 'active')
    
    def extract_invoice_data(self, raw_invoice: Dict) -> Dict:
        """Extract and normalize invoice data from Simply-CRM format"""
        return {
            'simplycrm_id': raw_invoice.get('id'),
            'invoice_number': raw_invoice.get('invoice_no'),
            'invoice_date': raw_invoice.get('invoicedate'),
            'account_id': raw_invoice.get('account_id'),
            'status': self._map_invoice_status(raw_invoice.get('invoicestatus')),
            'subtotal': self._parse_amount(raw_invoice.get('hdnSubTotal')),
            'tax_amount': self._parse_amount(raw_invoice.get('hdnTax')),
            'total_amount': self._parse_amount(raw_invoice.get('hdnGrandTotal')),
            'currency': raw_invoice.get('currency_id', 'DKK'),
            'line_items': raw_invoice.get('LineItems', []),
            'subscription_period': raw_invoice.get('cf_subscription_period'),
            'is_subscription': raw_invoice.get('invoicestatus') == 'Auto Created',
            'modified_time': raw_invoice.get('modifiedtime'),
        }
    
    def _map_subscription_status(self, status: Optional[str]) -> str:
        """Map Simply-CRM subscription status to OmniSync status"""
        if not status:
            return 'active'
        status_map = {
            'Active': 'active',
            'Cancelled': 'cancelled',
            'Expired': 'expired',
            'Suspended': 'suspended',
            'Pending': 'draft',
        }
        return status_map.get(status, 'active')
    
    def _map_invoice_status(self, status: Optional[str]) -> str:
        """Map Simply-CRM invoice status"""
        if not status:
            return 'active'
        status_map = {
            'Created': 'active',
            'Approved': 'active',
            'Sent': 'active',
            'Paid': 'paid',
            'Cancelled': 'cancelled',
            'Credit Invoice': 'credited',
            'Auto Created': 'active',
        }
        return status_map.get(status, 'active')
    
    def _map_billing_frequency(self, frequency: Optional[str]) -> str:
        """Map billing frequency to standard format"""
        if not frequency:
            return 'monthly'
        freq_map = {
            'Monthly': 'monthly',
            'Quarterly': 'quarterly',
            'Semi-annually': 'semi_annual',
            'Annually': 'yearly',
            'Yearly': 'yearly',
        }
        return freq_map.get(frequency, 'monthly')
    
    def _parse_amount(self, value: Any) -> float:
        """Parse amount from string or number"""
        if value is None:
            return 0.0
        if isinstance(value, (int, float)):
            return float(value)
        try:
            cleaned = str(value).replace(' ', '').replace(',', '.').replace('DKK', '').replace('kr', '')
            return float(cleaned)
        except (ValueError, TypeError):
            return 0.0


# Singleton instance
simplycrm_service = SimplyCRMService()
