"""
Сервисы для интеграции с CRM системами
"""
import httpx
from typing import Dict, List, Any, Optional
from abc import ABC, abstractmethod
from datetime import datetime
from app.core.config import get_settings


class BaseCRMClient(ABC):
    """Базовый класс для CRM клиентов"""
    
    def __init__(self, config: Dict[str, Any]):
        self.config = config
        self.client = httpx.AsyncClient(timeout=30.0)
    
    @abstractmethod
    async def test_connection(self) -> bool:
        """Проверить подключение к CRM"""
        pass
    
    @abstractmethod
    async def get_contacts(self, limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]:
        """Получить список контактов из CRM"""
        pass
    
    @abstractmethod
    async def get_contact(self, contact_id: str) -> Dict[str, Any]:
        """Получить один контакт по ID"""
        pass
    
    @abstractmethod
    async def create_contact(self, contact_data: Dict[str, Any]) -> Dict[str, Any]:
        """Создать контакт в CRM"""
        pass
    
    @abstractmethod
    async def update_contact(self, contact_id: str, contact_data: Dict[str, Any]) -> Dict[str, Any]:
        """Обновить контакт в CRM"""
        pass
    
    def normalize_contact(self, crm_contact: Dict[str, Any]) -> Dict[str, Any]:
        """Нормализовать контакт из CRM в формат нашей системы"""
        return {
            "email": crm_contact.get("email"),
            "phone": crm_contact.get("phone"),
            "tags": crm_contact.get("tags", []),
            "meta": {
                "crm_id": crm_contact.get("id"),
                "crm_type": self.__class__.__name__,
                "crm_data": crm_contact
            }
        }
    
    async def close(self):
        """Закрыть HTTP клиент"""
        await self.client.aclose()


class AmoCRMClient(BaseCRMClient):
    """Клиент для работы с AmoCRM"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.api_url = config.get('api_url', '').rstrip('/')
        self.access_token = config.get('access_token')
        self.client_id = config.get('client_id')
        self.client_secret = config.get('client_secret')
        self.redirect_uri = config.get('redirect_uri')
        
        if not self.access_token:
            raise ValueError("Access token не найден. Интеграция не авторизована.")
    
    def _get_headers(self) -> Dict[str, str]:
        """Получить заголовки для запросов"""
        return {
            "Authorization": f"Bearer {self.access_token}",
            "Content-Type": "application/json"
        }
    
    async def test_connection(self) -> bool:
        """Проверить подключение к AmoCRM"""
        try:
            response = await self.client.get(
                f"{self.api_url}/api/v4/account",
                headers=self._get_headers()
            )
            return response.status_code == 200
        except Exception as e:
            print(f"AmoCRM connection error: {e}")
            return False
    
    async def refresh_access_token(self) -> Optional[str]:
        """Обновить access token через refresh token"""
        refresh_token = self.config.get('refresh_token')
        if not refresh_token:
            return None
        
        try:
            response = await self.client.post(
                f"{self.api_url}/oauth2/access_token",
                json={
                    "client_id": self.client_id,
                    "client_secret": self.client_secret,
                    "grant_type": "refresh_token",
                    "refresh_token": refresh_token,
                    "redirect_uri": self.redirect_uri
                }
            )
            
            if response.status_code == 200:
                data = response.json()
                self.access_token = data.get('access_token')
                return self.access_token
        except Exception as e:
            print(f"Token refresh error: {e}")
        
        return None
    
    async def get_contacts(self, limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]:
        """Получить список контактов из AmoCRM"""
        try:
            response = await self.client.get(
                f"{self.api_url}/api/v4/contacts",
                headers=self._get_headers(),
                params={"limit": limit, "page": offset // limit + 1}
            )
            
            if response.status_code == 401:
                # Попытка обновить токен
                if await self.refresh_access_token():
                    return await self.get_contacts(limit, offset)
            
            if response.status_code == 200:
                data = response.json()
                contacts = data.get('_embedded', {}).get('contacts', [])
                
                # Нормализация данных
                normalized = []
                for contact in contacts:
                    custom_fields = contact.get('custom_fields_values', [])
                    email = None
                    phone = None
                    
                    for field in custom_fields:
                        if field.get('field_code') == 'EMAIL':
                            values = field.get('values', [])
                            if values:
                                email = values[0].get('value')
                        elif field.get('field_code') == 'PHONE':
                            values = field.get('values', [])
                            if values:
                                phone = values[0].get('value')
                    
                    normalized.append({
                        "id": str(contact.get('id')),
                        "name": contact.get('name'),
                        "email": email,
                        "phone": phone,
                        "tags": [tag.get('name') for tag in contact.get('_embedded', {}).get('tags', [])],
                        "created_at": contact.get('created_at'),
                        "updated_at": contact.get('updated_at')
                    })
                
                return normalized
        except Exception as e:
            print(f"Error fetching AmoCRM contacts: {e}")
            raise
        
        return []
    
    async def get_contact(self, contact_id: str) -> Dict[str, Any]:
        """Получить один контакт из AmoCRM"""
        try:
            response = await self.client.get(
                f"{self.api_url}/api/v4/contacts/{contact_id}",
                headers=self._get_headers()
            )
            
            if response.status_code == 200:
                return response.json()
        except Exception as e:
            print(f"Error fetching AmoCRM contact: {e}")
            raise
        
        return {}
    
    async def create_contact(self, contact_data: Dict[str, Any]) -> Dict[str, Any]:
        """Создать контакт в AmoCRM"""
        # Формируем данные в формате AmoCRM
        amocrm_data = [
            {
                "name": contact_data.get('name', 'New Contact'),
                "custom_fields_values": []
            }
        ]
        
        if contact_data.get('email'):
            amocrm_data[0]['custom_fields_values'].append({
                "field_code": "EMAIL",
                "values": [{"value": contact_data['email'], "enum_code": "WORK"}]
            })
        
        if contact_data.get('phone'):
            amocrm_data[0]['custom_fields_values'].append({
                "field_code": "PHONE",
                "values": [{"value": contact_data['phone'], "enum_code": "WORK"}]
            })
        
        try:
            response = await self.client.post(
                f"{self.api_url}/api/v4/contacts",
                headers=self._get_headers(),
                json=amocrm_data
            )
            
            if response.status_code in [200, 201]:
                return response.json()
        except Exception as e:
            print(f"Error creating AmoCRM contact: {e}")
            raise
        
        return {}
    
    async def update_contact(self, contact_id: str, contact_data: Dict[str, Any]) -> Dict[str, Any]:
        """Обновить контакт в AmoCRM"""
        try:
            response = await self.client.patch(
                f"{self.api_url}/api/v4/contacts/{contact_id}",
                headers=self._get_headers(),
                json=contact_data
            )
            
            if response.status_code == 200:
                return response.json()
        except Exception as e:
            print(f"Error updating AmoCRM contact: {e}")
            raise
        
        return {}


class Bitrix24Client(BaseCRMClient):
    """Клиент для работы с Bitrix24 через единое приложение"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.domain = config.get('domain', '')
        self.access_token = config.get('access_token')
        self.settings = get_settings()
        
        if not self.access_token:
            raise ValueError("Access token не найден. Интеграция не авторизована.")
    
    def _get_url(self, method: str) -> str:
        """Получить URL для API метода"""
        return f"https://{self.domain}/rest/{method}"
    
    async def test_connection(self) -> bool:
        """Проверить подключение к Bitrix24"""
        try:
            response = await self.client.get(
                self._get_url('user.current'),
                params={"auth": self.access_token}
            )
            data = response.json()
            return 'result' in data
        except Exception as e:
            print(f"Bitrix24 connection error: {e}")
            return False
    
    async def get_contacts(self, limit: int = 50, offset: int = 0) -> List[Dict[str, Any]]:
        """Получить список контактов из Bitrix24"""
        try:
            response = await self.client.get(
                self._get_url('crm.contact.list'),
                params={
                    "auth": self.access_token,
                    "start": offset,
                    "filter": {},
                    "select": ["ID", "NAME", "LAST_NAME", "EMAIL", "PHONE"]
                }
            )
            
            if response.status_code == 200:
                data = response.json()
                contacts = data.get('result', [])
                
                # Нормализация
                normalized = []
                for contact in contacts:
                    email = None
                    phone = None
                    
                    # Извлекаем email и телефон из массивов
                    if contact.get('EMAIL'):
                        emails = contact['EMAIL']
                        if emails and isinstance(emails, list):
                            email = emails[0].get('VALUE')
                    
                    if contact.get('PHONE'):
                        phones = contact['PHONE']
                        if phones and isinstance(phones, list):
                            phone = phones[0].get('VALUE')
                    
                    normalized.append({
                        "id": str(contact.get('ID')),
                        "name": f"{contact.get('NAME', '')} {contact.get('LAST_NAME', '')}".strip(),
                        "email": email,
                        "phone": phone,
                        "tags": [],
                        "created_at": contact.get('DATE_CREATE'),
                        "updated_at": contact.get('DATE_MODIFY')
                    })
                
                return normalized
        except Exception as e:
            print(f"Error fetching Bitrix24 contacts: {e}")
            raise
        
        return []
    
    async def get_contact(self, contact_id: str) -> Dict[str, Any]:
        """Получить один контакт из Bitrix24"""
        try:
            response = await self.client.get(
                self._get_url('crm.contact.get'),
                params={"auth": self.access_token, "id": contact_id}
            )
            
            if response.status_code == 200:
                data = response.json()
                return data.get('result', {})
        except Exception as e:
            print(f"Error fetching Bitrix24 contact: {e}")
            raise
        
        return {}
    
    async def create_contact(self, contact_data: Dict[str, Any]) -> Dict[str, Any]:
        """Создать контакт в Bitrix24"""
        fields = {
            "NAME": contact_data.get('name', 'New Contact')
        }
        
        if contact_data.get('email'):
            fields['EMAIL'] = [{"VALUE": contact_data['email'], "VALUE_TYPE": "WORK"}]
        
        if contact_data.get('phone'):
            fields['PHONE'] = [{"VALUE": contact_data['phone'], "VALUE_TYPE": "WORK"}]
        
        try:
            response = await self.client.post(
                self._get_url('crm.contact.add'),
                params={"auth": self.access_token},
                json={"fields": fields}
            )
            
            if response.status_code == 200:
                return response.json()
        except Exception as e:
            print(f"Error creating Bitrix24 contact: {e}")
            raise
        
        return {}
    
    async def update_contact(self, contact_id: str, contact_data: Dict[str, Any]) -> Dict[str, Any]:
        """Обновить контакт в Bitrix24"""
        try:
            response = await self.client.post(
                self._get_url('crm.contact.update'),
                params={"auth": self.access_token, "id": contact_id},
                json={"fields": contact_data}
            )
            
            if response.status_code == 200:
                return response.json()
        except Exception as e:
            print(f"Error updating Bitrix24 contact: {e}")
            raise
        
        return {}
    
    async def refresh_access_token(self) -> Optional[str]:
        """Обновить access token через refresh token для Bitrix24"""
        refresh_token = self.config.get('refresh_token')
        if not refresh_token:
            return None
        
        if not self.settings.BITRIX_CLIENT_ID or not self.settings.BITRIX_CLIENT_SECRET:
            print("BITRIX_CLIENT_ID или BITRIX_CLIENT_SECRET не настроены на сервере")
            return None
        
        try:
            response = await self.client.post(
                "https://oauth.bitrix.info/oauth/token",
                data={
                    "grant_type": "refresh_token",
                    "client_id": self.settings.BITRIX_CLIENT_ID,
                    "client_secret": self.settings.BITRIX_CLIENT_SECRET,
                    "refresh_token": refresh_token
                }
            )
            
            if response.status_code == 200:
                data = response.json()
                self.access_token = data.get('access_token')
                return self.access_token
        except Exception as e:
            print(f"Bitrix24 token refresh error: {e}")
        
        return None


class OneC1Client(BaseCRMClient):
    """Клиент для работы с 1C"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.api_url = config.get('api_url', '').rstrip('/')
        self.username = config.get('username')
        self.password = config.get('password')
        self.auth = (self.username, self.password)
    
    async def test_connection(self) -> bool:
        """Проверить подключение к 1C"""
        try:
            response = await self.client.get(
                f"{self.api_url}/odata/standard.odata/Catalog_Контрагенты",
                auth=self.auth,
                params={"$top": 1}
            )
            return response.status_code == 200
        except Exception as e:
            print(f"1C connection error: {e}")
            return False
    
    async def get_contacts(self, limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]:
        """Получить список контактов из 1C"""
        try:
            response = await self.client.get(
                f"{self.api_url}/odata/standard.odata/Catalog_Контрагенты",
                auth=self.auth,
                params={
                    "$top": limit,
                    "$skip": offset,
                    "$select": "Ref_Key,Description,ИНН,КПП,Email,Телефон"
                }
            )
            
            if response.status_code == 200:
                data = response.json()
                contacts = data.get('value', [])
                
                # Нормализация
                normalized = []
                for contact in contacts:
                    normalized.append({
                        "id": contact.get('Ref_Key'),
                        "name": contact.get('Description'),
                        "email": contact.get('Email'),
                        "phone": contact.get('Телефон'),
                        "tags": [],
                        "inn": contact.get('ИНН'),
                        "kpp": contact.get('КПП')
                    })
                
                return normalized
        except Exception as e:
            print(f"Error fetching 1C contacts: {e}")
            raise
        
        return []
    
    async def get_contact(self, contact_id: str) -> Dict[str, Any]:
        """Получить один контакт из 1C"""
        try:
            response = await self.client.get(
                f"{self.api_url}/odata/standard.odata/Catalog_Контрагенты(guid'{contact_id}')",
                auth=self.auth
            )
            
            if response.status_code == 200:
                return response.json()
        except Exception as e:
            print(f"Error fetching 1C contact: {e}")
            raise
        
        return {}
    
    async def create_contact(self, contact_data: Dict[str, Any]) -> Dict[str, Any]:
        """Создать контакт в 1C"""
        # 1C обычно не поддерживает создание через OData напрямую
        # Требуется специфичная конфигурация
        raise NotImplementedError("Создание контактов в 1C не реализовано")
    
    async def update_contact(self, contact_id: str, contact_data: Dict[str, Any]) -> Dict[str, Any]:
        """Обновить контакт в 1C"""
        # 1C обычно не поддерживает обновление через OData напрямую
        # Требуется специфичная конфигурация
        raise NotImplementedError("Обновление контактов в 1C не реализовано")


def get_crm_client(integration_type: str, config: Dict[str, Any]) -> BaseCRMClient:
    """Фабрика для создания CRM клиента"""
    clients = {
        'amocrm': AmoCRMClient,
        'bitrix24': Bitrix24Client,
        '1c': OneC1Client,
    }
    
    client_class = clients.get(integration_type)
    if not client_class:
        raise ValueError(f"Неподдерживаемый тип CRM: {integration_type}")
    
    return client_class(config)

