"""AnyMessage e-posta servisi ile etkileşim için yardımcılar."""
from __future__ import annotations

import json
import logging
import re
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Callable, Dict, Optional, Tuple

import requests

try:  # pragma: no cover - stdlib uyumluluğu
    from json import JSONDecodeError as _StdJSONDecodeError
except Exception:  # pragma: no cover - eski Python sürümleri
    _StdJSONDecodeError = ValueError

try:  # pragma: no cover - requests sürümü uyumluluğu
    from requests.exceptions import JSONDecodeError as _RequestsJSONDecodeError
except Exception:  # pragma: no cover - bazı sürümlerde mevcut değil
    _RequestsJSONDecodeError = ValueError

_JSON_DECODE_ERRORS: Tuple[type, ...] = (
    _StdJSONDecodeError,
    _RequestsJSONDecodeError,
)

from mail_stats import record_mail_check_result, record_mail_order_result


CONFIG_PATH = Path(__file__).with_name("config.json")
API_KEY_FIELD = "AnyMessage"
EMAIL_DOMAIN_FIELD = "anymessage_domain"
DEFAULT_EMAIL_DOMAIN = "gmail.com"

BASE_URL = "https://api.anymessage.shop"
ORDER_ENDPOINT = f"{BASE_URL}/email/order"
GET_MESSAGE_ENDPOINT = f"{BASE_URL}/email/getmessage"
DELETE_MESSAGE_ENDPOINT = f"{BASE_URL}/email/deletemessage"
REORDER_ENDPOINT = f"{BASE_URL}/email/reorder"
BALANCE_ENDPOINT = f"{BASE_URL}/user/balance"
QUANTITY_ENDPOINT = f"{BASE_URL}/email/quantity"

CODE_REGEX = re.compile(r"(?<!\\d)(\\d{6})(?!\\d)")


class AnyMessageConfigError(RuntimeError):
    """config.json içindeki AnyMessage ayarlarıyla ilgili hataları temsil eder."""


class EmailBannedError(RuntimeError):
    """AnyMessage yeniden sipariş isteği sırasında dönen "email banned" hatası."""

    def __init__(
        self,
        status_code: int,
        payload_status: str,
        body: str,
        parsed: Optional[Dict[str, Any]],
    ) -> None:
        super().__init__("email banned")
        self.status_code = int(status_code or 0)
        self.payload_status = str(payload_status or "")
        self.body = body
        self.parsed = dict(parsed) if isinstance(parsed, dict) else None


def _load_config_data(config_path: Path = CONFIG_PATH) -> Dict[str, Any]:
    """config.json dosyasını sözlük olarak yükle."""

    if not config_path.exists():
        raise FileNotFoundError(f"Config dosyası bulunamadı: {config_path}")

    with config_path.open("r", encoding="utf-8") as config_file:
        data = json.load(config_file)

    if not isinstance(data, dict):
        raise AnyMessageConfigError("config.json beklenmeyen içerik döndürdü")

    return data


def load_api_key(config_path: Path = CONFIG_PATH, *, field_name: str = API_KEY_FIELD) -> str:
    """config.json içindeki AnyMessage API anahtarını döndür."""

    data = _load_config_data(config_path)

    api_key = data.get(field_name)
    if not isinstance(api_key, str) or not api_key.strip():
        raise AnyMessageConfigError(
            f"config.json içinde '{field_name}' anahtarı bulunamadı veya geçersiz."
        )

    return api_key.strip()


def load_email_domain(
    config_path: Path = CONFIG_PATH,
    *,
    field_name: str = EMAIL_DOMAIN_FIELD,
    default: str = DEFAULT_EMAIL_DOMAIN,
) -> str:
    """AnyMessage e-posta siparişi için alan adını döndür."""

    data = _load_config_data(config_path)
    domain = data.get(field_name)
    if isinstance(domain, str):
        domain = domain.strip()
        if domain:
            return domain

    return default


def fetch_email_quantity(api_key: str, *, site: str = "x.com") -> Dict[str, Any]:
    """AnyMessage stok bilgilerini döndür."""

    if not api_key:
        raise ValueError("AnyMessage API anahtarı bulunamadı")

    params = {"token": api_key, "site": site}
    response = requests.get(QUANTITY_ENDPOINT, params=params, timeout=30)
    response.raise_for_status()

    payload = response.json()
    if not isinstance(payload, dict):
        raise ValueError("AnyMessage stok sorgusu beklenmeyen içerik döndürdü")

    status = str(payload.get("status", "")).strip().lower()
    if status and status not in {"success", "ok"}:
        raise ValueError(f"AnyMessage stok sorgusu hata döndürdü: {payload}")

    return payload


def extract_domain_count(quantity_payload: Dict[str, Any], domain: str) -> Optional[int]:
    """Stok yanıtından belirtilen domain için count değerini çıkar."""

    if not isinstance(quantity_payload, dict):
        return None

    domain_key = str(domain or "").strip().lower()
    data = quantity_payload.get("data")
    if not isinstance(data, dict):
        return None

    for key, value in data.items():
        if str(key).strip().lower() != domain_key:
            continue
        if isinstance(value, dict):
            count_value = value.get("count")
        else:
            count_value = None
        try:
            return int(count_value)
        except (TypeError, ValueError):
            return None

    return None


def get_available_domain_count(
    api_key: str,
    *,
    domain: str = DEFAULT_EMAIL_DOMAIN,
    site: str = "x.com",
) -> Optional[int]:
    """Belirtilen domain için anlık stok adetini döndür."""

    payload = fetch_email_quantity(api_key, site=site)
    return extract_domain_count(payload, domain)


def get_available_domain_count_with_retry(
    api_key: str,
    *,
    domain: str = DEFAULT_EMAIL_DOMAIN,
    site: str = "x.com",
    attempts: int = 3,
    retry_delay: float = 1.0,
    logger: Optional[logging.Logger] = None,
) -> Optional[int]:
    """AnyMessage stok sorgusunu hatalarda tekrar dene."""

    if attempts <= 0:
        attempts = 1

    last_error: Optional[BaseException] = None
    for attempt in range(1, attempts + 1):
        try:
            return get_available_domain_count(
                api_key,
                domain=domain,
                site=site,
            )
        except Exception as exc:  # pragma: no cover - dış servis
            last_error = exc
            if logger is not None:
                logger.warning(
                    "AnyMessage stock query failed on attempt %d/%d: %s",
                    attempt,
                    attempts,
                    exc,
                )

            if attempt < attempts:
                time.sleep(max(retry_delay, 0))

    if logger is not None and last_error is not None:
        logger.warning(
            "AnyMessage stock information could not be obtained after %d attempts: %s",
            attempts,
            last_error,
        )
    return None


def fetch_balance(*, config_path: Path = CONFIG_PATH) -> Dict[str, Any]:
    """Mail servisi bakiyesini config.json'daki anahtarı kullanarak döndür."""

    api_key = load_api_key(config_path)
    params = {"token": api_key}
    response = requests.get(BALANCE_ENDPOINT, params=params, timeout=30)
    response.raise_for_status()

    data = response.json()
    if not isinstance(data, dict):
        raise ValueError("Mail servisi beklenmeyen yanıt döndürdü.")

    status = str(data.get("status", "")).lower()
    if status != "success":
        raise ValueError(f"Mail servisi hatası: {data}")

    return data


@dataclass
class EmailOrderResult:
    ok: bool
    status_code: int
    text: str
    data: Optional[Dict[str, Any]]
    error: Optional[str] = None


def order_email(api_key: str) -> EmailOrderResult:
    """AnyMessage üzerinde yeni bir e-posta sipariş et."""

    if not api_key:
        result = EmailOrderResult(False, 0, "Missing API key", None, "missing_api_key")
        record_mail_order_result(False)
        return result

    try:
        domain = load_email_domain()
    except Exception as exc:  # pragma: no cover - beklenmeyen konfigürasyon hataları
        err = str(exc)
        result = EmailOrderResult(False, 0, err, None, err)
        record_mail_order_result(False)
        return result

    url = f"{ORDER_ENDPOINT}?token={api_key}&site=x.com&domain={domain}&soft_id=3219"

    try:
        resp = requests.get(url, timeout=40)
        text = resp.text
        try:
            data = resp.json()
        except Exception:
            data = None

        ok = (
            resp.status_code == 200
            and isinstance(data, dict)
            and str(data.get("status", "")).lower() == "success"
        )

        error_value: Optional[str] = None
        if not ok:
            api_message: Optional[str] = None
            if isinstance(data, dict):
                for key in (
                    "message",
                    "error",
                    "detail",
                    "description",
                    "status_message",
                    "statusMessage",
                    "msg",
                ):
                    value = data.get(key)
                    if isinstance(value, (str, int, float)):
                        api_message = str(value)
                        break

            if not api_message:
                summary = text.strip()
                if summary:
                    summary = " ".join(summary.split())
                    if len(summary) > 200:
                        summary = summary[:197] + "..."
                    api_message = summary or None

            if not api_message:
                api_message = "Bilinmeyen API hatası"

            status_text = f"HTTP {resp.status_code}" if resp.status_code else "HTTP ?"
            error_value = f"{status_text} - {api_message}"

        result = EmailOrderResult(ok, resp.status_code, text, data, error_value)
    except Exception as exc:  # pragma: no cover - ağ hataları test dışı bırakılır
        err = str(exc)
        result = EmailOrderResult(False, 0, err, None, err)

    record_mail_order_result(result.ok)
    return result


@dataclass
class MailCheckResult:
    ok: bool
    status_code: int
    text: str
    data: Optional[Dict[str, Any]]
    attempts: int
    error: Optional[str] = None
    code: Optional[str] = None


def _extract_verification_code(mail_data: Optional[Dict[str, Any]], raw_text: str) -> Optional[str]:
    if isinstance(mail_data, dict):
        value = str(mail_data.get("value", "")).strip()
        if value.isdigit() and len(value) == 6:
            return value

        message = mail_data.get("message", "")
        if isinstance(message, str):
            match = CODE_REGEX.search(message)
            if match:
                return match.group(1)

    if isinstance(raw_text, str):
        match = CODE_REGEX.search(raw_text)
        if match:
            return match.group(1)

    return None


def _delete_email_message(
    api_key: str,
    email_id: str,
    *,
    log_fn: Optional[Callable[[str], None]] = None,
    verbose: bool = False,
) -> Optional[str]:
    if not api_key or not email_id:
        return "missing_credentials"

    url = f"{DELETE_MESSAGE_ENDPOINT}?token={api_key}&id={email_id}"

    try:
        resp = requests.get(url, timeout=30)
        text = resp.text
        try:
            parsed = resp.json()
        except Exception:
            parsed = None

        status = str((parsed or {}).get("status", "")).lower()
        if resp.status_code == 200 and status == "success":
            if log_fn:
                log_fn("[AnyMessage] delete message → success")
            elif verbose:
                print("[AnyMessage] delete message → success")
            return None

        message = (
            f"[AnyMessage] delete message failed status_code={resp.status_code} "
            f"payload_status={status} body='{text[:120]}'"
        )
        if log_fn:
            log_fn(message)
        elif verbose:
            print(message)
        return message
    except Exception as exc:  # pragma: no cover - ağ hataları test dışı
        err = str(exc)
        if log_fn:
            log_fn(f"[AnyMessage] delete message exception={err}")
        elif verbose:
            print(f"[AnyMessage] delete message exception={err}")
        return err


def _is_success_payload(parsed: Optional[Dict[str, Any]]) -> bool:
    if not isinstance(parsed, dict):
        return False
    return str(parsed.get("status", "")).lower() == "success"


def wait_for_email(
    api_key: str,
    email_id: str,
    *,
    timeout_sec: int = 180,
    interval_sec: int = 2,
    verbose: bool = False,
    log_fn: Optional[Callable[[str], None]] = None,
    reject_code: Optional[str] = None,
    no_activation_limit: int = 5,
    log_progress: bool = False,
) -> MailCheckResult:
    """AnyMessage üzerinde sipariş edilen e-postadaki doğrulama kodunu bekle."""

    def _finalise(result: MailCheckResult) -> MailCheckResult:
        record_mail_check_result(result.ok)
        return result

    progress_logging = bool(log_progress or verbose)

    def _emit(message: str, *, progress: bool = False) -> None:
        if progress and not progress_logging:
            return
        if log_fn:
            log_fn(message)
        elif verbose:
            print(message)

    if not api_key:
        return _finalise(
            MailCheckResult(False, 0, "Missing API key", None, 0, "missing_api_key")
        )
    if not email_id:
        return _finalise(
            MailCheckResult(False, 0, "Missing email_id", None, 0, "missing_email_id")
        )

    url = f"{GET_MESSAGE_ENDPOINT}?token={api_key}&id={email_id}&preview=0"

    deadline = time.time() + timeout_sec
    attempts = 0
    last_err: Optional[str] = None
    reorder_attempted = False
    reorder_succeeded = False
    no_activation_count = 0
    activation_limit = max(0, int(no_activation_limit))
    consecutive_json_errors = 0

    while time.time() < deadline:
        attempts += 1
        try:
            resp = requests.get(url, timeout=30)
            text = resp.text
            try:
                parsed: Optional[Dict[str, Any]] = resp.json()
                consecutive_json_errors = 0
            except _JSON_DECODE_ERRORS as decode_error:
                consecutive_json_errors += 1
                parsed = None
                decode_message = (
                    "[AnyMessage] attempt="
                    f"{attempts} json decode hatası: {decode_error}"
                )
                last_err = decode_message
                _emit(decode_message, progress=True)

                if consecutive_json_errors >= 3:
                    termination_message = (
                        "[AnyMessage] art arda 3 JSON çözümleme hatası alındı; mail kontrolü sonlandırılıyor."
                    )
                    _emit(termination_message)

                    error_payload: Dict[str, Any] = {
                        "json_decode_error": True,
                        "consecutive_json_errors": consecutive_json_errors,
                        "response_status_code": resp.status_code,
                    }
                    if text:
                        error_payload["response_preview"] = text[:200]

                    return _finalise(
                        MailCheckResult(
                            False,
                            resp.status_code,
                            text,
                            error_payload,
                            attempts,
                            "json_decode_error",
                        )
                    )

                time.sleep(interval_sec)
                continue
            except Exception:
                parsed = None
                consecutive_json_errors = 0

            payload_status = str((parsed or {}).get("status", "")).lower()
            if resp.status_code == 200 and _is_success_payload(parsed):
                code = _extract_verification_code(parsed, text)
                if reject_code and code and code == reject_code:
                    duplicate_msg = (
                        "[AnyMessage] duplicate verification code received; aborting further attempts."
                    )
                    _emit(duplicate_msg)
                    _delete_email_message(api_key, email_id, log_fn=log_fn, verbose=verbose)
                    return _finalise(
                        MailCheckResult(
                            False,
                            resp.status_code,
                            text,
                            parsed,
                            attempts,
                            "duplicate_code",
                            code,
                        )
                    )
                _emit(f"[AnyMessage] attempt={attempts} status=success {resp.status_code}")
                return _finalise(
                    MailCheckResult(True, resp.status_code, text, parsed, attempts, None, code)
                )

            message = (
                f"[AnyMessage] attempt={attempts} status_code={resp.status_code} "
                f"payload_status={payload_status} body='{text[:120]}'"
            )
            _emit(message, progress=True)
            last_err = message
            if (
                resp.status_code == 200
                and payload_status == "error"
                and isinstance(parsed, dict)
                and str(parsed.get("value", "")).strip().lower() == "no activation"
            ):
                no_activation_count += 1
                if activation_limit and no_activation_count >= activation_limit:
                    limit_message = (
                        "[AnyMessage] no activation limit reached; aborting mail check."
                    )
                    _emit(limit_message)

                    if isinstance(parsed, dict):
                        parsed = dict(parsed)
                    else:
                        parsed = {}
                    parsed.setdefault("no_activation_limit_reached", True)
                    parsed["no_activation_count"] = no_activation_count

                    return _finalise(
                        MailCheckResult(
                            False,
                            resp.status_code,
                            text,
                            parsed,
                            attempts,
                            "no_activation_limit",
                        )
                    )

                if not reorder_attempted:
                    reorder_attempted = True
                    trigger_message = "[AnyMessage] reorder_email triggered"
                    _emit(trigger_message, progress=True)

                    try:
                        reorder_succeeded = reorder_email(
                            api_key,
                            email_id,
                            log_fn=log_fn if progress_logging else None,
                            verbose=verbose,
                        )
                    except EmailBannedError as banned_error:
                        termination_message = (
                            "[AnyMessage] email banned yanıtı alındı; mail kontrolü sonlandırılıyor."
                        )
                        _emit(termination_message)

                        response_data: Dict[str, Any] = {
                            "status": "error",
                            "value": "email banned",
                            "reorder_status_code": banned_error.status_code,
                            "reorder_payload_status": banned_error.payload_status,
                        }
                        if banned_error.parsed:
                            response_data.update(banned_error.parsed)

                        return _finalise(
                            MailCheckResult(
                                False,
                                banned_error.status_code,
                                str(banned_error.body or ""),
                                response_data,
                                attempts,
                                "email_banned",
                            )
                        )

                    result_message = (
                        "[AnyMessage] reorder_email result="
                        f"{'success' if reorder_succeeded else 'failure'}"
                    )
                    _emit(result_message, progress=True)

                    last_err = None if reorder_succeeded else result_message

                    time.sleep(interval_sec)
                    continue

                if reorder_attempted and isinstance(parsed, dict):
                    parsed = dict(parsed)
                    parsed.setdefault("reordered", reorder_succeeded)

                if reorder_attempted and not reorder_succeeded:
                    time.sleep(interval_sec)
                    continue

                return _finalise(
                    MailCheckResult(
                        False,
                        resp.status_code,
                        text,
                        parsed,
                        attempts,
                        "no_activation",
                    )
                )
        except EmailBannedError as exc:
            message = f"[AnyMessage] attempt={attempts} email banned; aborting."
            _emit(message)
            return _finalise(
                MailCheckResult(
                    False,
                    exc.status_code,
                    str(exc.body or ""),
                    exc.parsed or {"status": "error", "value": "email banned"},
                    attempts,
                    "email_banned",
                )
            )

        except Exception as exc:  # pragma: no cover - ağ hataları test dışı
            last_err = str(exc)
            _emit(f"[AnyMessage] attempt={attempts} exception={last_err}", progress=True)

        time.sleep(interval_sec)

    return _finalise(
        MailCheckResult(False, 0, f"timeout after {attempts} attempts", None, attempts, last_err)
    )


def reorder_email(
    api_key: str,
    email_id: str,
    *,
    log_fn: Optional[Callable[[str], None]] = None,
    verbose: bool = False,
) -> bool:
    """AnyMessage servisinde belirli bir e-posta için yeniden sipariş ver."""

    if not api_key or not email_id:
        return False

    url = f"{REORDER_ENDPOINT}?token={api_key}&id={email_id}"

    try:
        resp = requests.get(url, timeout=30)
        text = resp.text
        try:
            parsed = resp.json()
        except Exception:
            parsed = None

        status = str((parsed or {}).get("status", "")).lower()
        if resp.status_code == 200 and status == "success":
            if log_fn:
                log_fn(
                    f"[AnyMessage] reorder request succeeded status_code={resp.status_code}"
                )
            elif verbose:
                print(
                    f"[AnyMessage] reorder request succeeded status_code={resp.status_code}"
                )
            return True

        message = (
            "[AnyMessage] reorder request failed "
            f"status_code={resp.status_code} payload_status={status} "
            f"body='{text[:120]}'"
        )
        if log_fn:
            log_fn(message)
        elif verbose:
            print(message)

        value_field = ""
        if isinstance(parsed, dict):
            value_field = str(parsed.get("value") or "").strip().lower()
        if status == "error" and value_field == "email banned":
            raise EmailBannedError(resp.status_code, status, text, parsed)
    except EmailBannedError:
        raise
    except Exception as exc:  # pragma: no cover - ağ hataları test dışı
        err = str(exc)
        if log_fn:
            log_fn(f"[AnyMessage] reorder request exception={err}")
        elif verbose:
            print(f"[AnyMessage] reorder request exception={err}")

    return False


__all__ = [
    "AnyMessageConfigError",
    "EmailOrderResult",
    "MailCheckResult",
    "fetch_balance",
    "fetch_email_quantity",
    "extract_domain_count",
    "get_available_domain_count",
    "load_api_key",
    "order_email",
    "reorder_email",
    "wait_for_email",
]
