import re
import requests
from typing import Dict, List, Optional, Any
from requests.exceptions import Timeout
from .exceptions import (
    SamotpravilError, AuthorizationError,
    BadRequestError, StopListError,
    DomainNotTrustedError, BadEmailError
)


class SamotpravilClient:
    def __init__(self, api_key: str, base_url: str = 'https://api.samotpravil.ru', timeout=10):
        self.api_key = api_key
        self.base_url = base_url
        self.timeout = timeout
        self.headers = self._get_headers()

    def _get_headers(self) -> Dict[str, str]:
        return {
            'Authorization': self.api_key,
            'Content-Type': 'application/json'
        }

    def _handle_response(self, response: requests.Response) -> Any:
        if response.status_code == 200:
            data = response.json()
            if data.get('status').lower() == 'ok':
                return data
            elif data.get('code') == 550:
                raise StopListError(data.get('message'))
            elif data.get('code') == 501:
                raise DomainNotTrustedError(data.get('message'))
            elif data.get('code') == 553:
                raise BadEmailError(data.get('message'))
            else:
                raise SamotpravilError(data.get('message'))
        elif response.status_code == 403:
            raise AuthorizationError(response.json().get('message'))
        elif response.status_code == 400:
            raise BadRequestError(response.json().get('message'))
        else:
            response.raise_for_status()

    def _post(self, endpoint: str, data: Dict[str, Any]) -> Any:
        url = f"{self.base_url}{endpoint}"
        try:
            response = requests.post(url, json=data, headers=self.headers, timeout=self.timeout)
        except Timeout:
            raise SamotpravilError("Request timed out")
        return self._handle_response(response)

    def _get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Any:
        url = f"{self.base_url}{endpoint}"
        try:
            response = requests.get(url, params=params, headers=self.headers, timeout=self.timeout)
        except Timeout:
            raise SamotpravilError("Request timed out")
        return self._handle_response(response)

    def _validate_email(self, email: str) -> None:
        email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+")
        if not email_regex.match(email):
            raise ValueError(f"Invalid email address: {email}")

    def send_email(
            self,
            email_from: str,
            email_to: str,
            subject: str,
            message_text: Optional[str] = None,
            template_id: Optional[str] = None,
            mg_api_key: Optional[str] = None,
            **kwargs: Any
        ) -> Any:
        '''
        https://documentation.samotpravil.ru/view/26779685/2s93RZM9in#d38df6bc-71da-4445-a1eb-bbc299138836

        Единичная отправка письма
        - `email_from`: Email отправителя
        - `email_to`: Email получателя
        - `subject`: Тема письма
        - `message_text`: Текст письма
        - `template_id`: ID шаблона письма в Mailganer. Если передан, то нужно добавить заголовок `Mg-Api-Key` с API-ключом от Mailganer
        - `mg_api_key`: API-ключ от Mailganer
        

        Optional kwargs:
        - `name_from`: Имя отправителя
        - `params` (dict): дополнительные параметры письма
        - `x_track_id` (str): уникальный идентификатор письма для трекинга
        - `track_open` (bool): флаг для отслеживания открытия письма
        - `track_click` (bool): флаг для отслеживания кликов по ссылкам в письме
        - `track_domain` (str): домен для трекинга
        - `check_stop_list` (bool): флаг для проверки стоп-листа
        - `check_local_stop_list` (bool): флаг для проверки локального стоп-листа
        - `domain_for_dkim` (str): домен для DKIM подписи
        - `headers` (dict): дополнительные заголовки письма
        '''
        self._validate_email(email_to)
        self._validate_email(email_from)

        endpoint = "/api/v1/smtp_send"
        data = {
            "email_to": email_to,
            "subject": subject,
            "email_from": f"{kwargs.get('name_from')} <{email_from}>" if kwargs.get('name_from') else email_from,
        }
        # Проверка наличия контента письма или шаблона
        if not template_id and not message_text:
            raise ValueError("Either message_text or template_id must be provided")
        
        # Добавление контента письма, если он есть
        if message_text:
            data['message_text'] = message_text

        # Добавление необязательных параметров
        optional_fields = ["params", "x_track_id", "track_open", "track_click", "track_domain",
                           "check_stop_list", "check_local_stop_list", "domain_for_dkim", "headers"]
        for field in optional_fields:
            if kwargs.get(field) is not None:
                data[field] = kwargs[field]

        # Обработка шаблона Mailganer
        if template_id is not None:
            if not mg_api_key:
                raise ValueError("mg_api_key is required when template_id is provided")
            self.headers['Mg-Api-Key'] = mg_api_key
            data['template_id'] = template_id
            
        return self._post(endpoint, data)

    def get_status(self, **kwargs: Any) -> Any:
        endpoint = "/api/v2/issue/status"
        params = {key: value for key, value in kwargs.items() if value is not None}
        return self._get(endpoint, params)

    def get_statistics(self, date_from: str, date_to: str, **kwargs: Any) -> Any:
        endpoint = "/api/v2/issue/statistics"
        params = {
            "date_from": date_from,
            "date_to": date_to,
            "limit": kwargs.get('limit', 100),
            "cursor_next": kwargs.get('cursor_next')
        }
        return self._get(endpoint, params)
    
    def send_package(
            self,
            email_from: str,
            name_from: str,
            subject: str,
            message_text: str,
            users: List[Dict],
            **kwargs: Any
        ) -> Any:
        '''
        https://documentation.samotpravil.ru/view/26779685/2s93RZM9in#91fa5d31-0228-48ed-81b1-f578a21d325b

        Массовая отправка писем
        - `email_from`: Email отправителя
        - `name_from`: Имя отправителя
        - `subject`: Тема письма
        - `message_text`: Текст письма
        - `users`: Список получателей. Каждый получатель - словарь с ключами:
            - `emailto`: Email получателя *обязательный*
            - `field1` (optional): любое дополнительное поле для персонализации письма. Подробнее в документации.

        Optional kwargs:
        - `message_text_amp` (str): Текст письма в формате AMP
        - `check_local_stop_list` (bool): флаг для проверки локального стоп-листа
        - `check_global_stop_list` (bool): флаг для проверки глобального стоп-листа
        - `track_open` (bool): флаг для отслеживания открытия письма
        - `track_click` (bool): флаг для отслеживания кликов по ссылкам в письме
        - `track_domain` (str): домен для трекинга
        - `headers` (dict): дополнительные заголовки письма
        - `is_moderate` (bool): флаг для модерации письма
        - `html_prettify` (bool): флаг для автоматического форматирования HTML письма
        '''
        self._validate_email(email_from)

        endpoint = f"/api/v1/add_json_package?key={self.api_key}"
        data = {
            "email_from": email_from,
            "name_from": name_from,
            "subject": subject,
            "message_text": message_text,
            "users": users
        }
        
        optional_fields = ["message_text_amp", "check_local_stop_list", "check_global_stop_list",
                           "track_open", "track_click", "track_domain", "headers", "is_moderate", "html_prettify"]
        for field in optional_fields:
            if kwargs.get(field) is not None:
                data[field] = kwargs[field]

        return self._post(endpoint, data)
    
    def stop_package(self, package_id: str) -> Any:
        endpoint = "/api/v1/package_stop"
        params = {"pack_id": package_id}
        return self._get(endpoint, params)
    
    def get_package_status(self, package_id: str) -> Any:
        endpoint = "/api/v2/package/status"
        params = {"issuen": package_id}
        return self._get(endpoint, params)

    def get_non_delivery_by_date(self, date_from: str, date_to: str, **kwargs: Any) -> Any:
        endpoint = "/api/v2/blist/report/non-delivery"
        params = {
            "date_from": date_from,
            "date_to": date_to,
            "limit": kwargs.get('limit', 100),
            "cursor_next": kwargs.get('cursor_next')
        }
        return self._get(endpoint, params)

    def get_non_delivery_by_issue(self, issuen: str, **kwargs: Any) -> Any:
        endpoint = "/api/v2/issue/report/non-delivery"
        params = {
            "issuen": issuen,
            "limit": kwargs.get('limit', 100),
            "cursor_next": kwargs.get('cursor_next')
        }
        return self._get(endpoint, params)

    def get_fbl_report_by_date(self, date_from: str, date_to: str, **kwargs: Any) -> Any:
        endpoint = "/api/v2/blist/report/fbl"
        params = {
            "date_from": date_from,
            "date_to": date_to,
            "limit": kwargs.get('limit', 100),
            "cursor_next": kwargs.get('cursor_next')
        }
        return self._get(endpoint, params)

    def get_fbl_report_by_issue(self, issuen: str, **kwargs: Any) -> Any:
        endpoint = "/api/v2/issue/report/fbl"
        params = {
            "issuen": issuen,
            "limit": kwargs.get('limit', 100),
            "cursor_next": kwargs.get('cursor_next')
        }
        return self._get(endpoint, params)

    def stop_list_search(self, email: str) -> Any:
        self._validate_email(email)
        endpoint = "/api/v2/stop-list/search"
        params = {"email": email}
        return self._get(endpoint, params)

    def stop_list_add(self, email: str, domain: str) -> Any:
        self._validate_email(email)
        endpoint = "/api/v2/stop-list/add"
        data = {"email": email, "mail_from": f"info@{domain}"}
        return self._post(endpoint, data)

    def stop_list_remove(self, email: str, domain: str) -> Any:
        self._validate_email(email)
        endpoint = "/api/v2/stop-list/remove"
        data = {"email": email, "mail_from": f"info@{domain}"}
        return self._post(endpoint, data)

    def get_domains(self) -> Any:
        endpoint = "/api/v2/blist/domains"
        return self._get(endpoint)

    def domain_add(self, domain: str) -> Any:
        endpoint = "/api/v2/blist/domains/add"
        data = {"domain": domain}
        return self._post(endpoint, data)

    def domain_remove(self, domain: str) -> Any:
        endpoint = "/api/v2/blist/domains/remove"
        data = {"domain": domain}
        return self._post(endpoint, data)

    def domain_check_verification(self, domain: str) -> Any:
        endpoint = "/api/v2/blist/domains/verify"
        data = {"domain": domain}
        return self._post(endpoint, data)
