"""
Synor Hosting SDK Client
"""

from typing import Any, Dict, List, Optional
from urllib.parse import urlencode
import httpx

from .types import (
    HostingConfig,
    Network,
    Domain,
    DomainRecord,
    DomainStatus,
    DomainAvailability,
    DnsRecord,
    DnsRecordType,
    DnsZone,
    Deployment,
    DeploymentStatus,
    DeploymentStats,
    RedirectRule,
    Certificate,
    CertificateStatus,
    SiteConfig,
    AnalyticsData,
    PageView,
    Referrer,
    Country,
    HostingError,
)


class SynorHosting:
    """
    Synor Hosting SDK Client.

    Provides domain registration, DNS management, site deployment, and SSL provisioning.
    """

    def __init__(
        self,
        api_key: str,
        endpoint: str = "https://hosting.synor.io/v1",
        network: Network = Network.MAINNET,
        timeout: float = 60.0,
        retries: int = 3,
        debug: bool = False,
    ):
        self._config = HostingConfig(
            api_key=api_key,
            endpoint=endpoint,
            network=network,
            timeout=timeout,
            retries=retries,
            debug=debug,
        )
        self._client = httpx.AsyncClient(
            base_url=endpoint,
            timeout=timeout,
            headers={
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json",
                "X-SDK-Version": "python/0.1.0",
            },
        )
        self._closed = False

    # ==================== Domain Operations ====================

    async def check_availability(self, name: str) -> DomainAvailability:
        """Check domain availability."""
        response = await self._request("GET", f"/domains/check/{name}")
        return DomainAvailability(
            name=response["name"],
            available=response["available"],
            price=response.get("price"),
            premium=response.get("premium", False),
        )

    async def register_domain(
        self,
        name: str,
        years: int = 1,
        auto_renew: bool = True,
        records: Optional[DomainRecord] = None,
    ) -> Domain:
        """Register a new domain."""
        body: Dict[str, Any] = {"name": name, "years": years, "auto_renew": auto_renew}
        if records:
            body["records"] = self._domain_record_to_dict(records)

        response = await self._request("POST", "/domains", body=body)
        return self._parse_domain(response)

    async def get_domain(self, name: str) -> Domain:
        """Get domain information."""
        response = await self._request("GET", f"/domains/{name}")
        return self._parse_domain(response)

    async def list_domains(self) -> List[Domain]:
        """List all domains."""
        response = await self._request("GET", "/domains")
        return [self._parse_domain(d) for d in response.get("domains", [])]

    async def update_domain_record(self, name: str, record: DomainRecord) -> Domain:
        """Update domain record."""
        response = await self._request(
            "PUT", f"/domains/{name}/record", body=self._domain_record_to_dict(record)
        )
        return self._parse_domain(response)

    async def resolve_domain(self, name: str) -> DomainRecord:
        """Resolve a domain to its record."""
        response = await self._request("GET", f"/domains/{name}/resolve")
        return self._parse_domain_record(response)

    async def renew_domain(self, name: str, years: int = 1) -> Domain:
        """Renew a domain."""
        response = await self._request("POST", f"/domains/{name}/renew", body={"years": years})
        return self._parse_domain(response)

    async def transfer_domain(self, name: str, new_owner: str) -> Domain:
        """Transfer domain ownership."""
        response = await self._request("POST", f"/domains/{name}/transfer", body={"new_owner": new_owner})
        return self._parse_domain(response)

    # ==================== DNS Operations ====================

    async def get_dns_zone(self, domain: str) -> DnsZone:
        """Get DNS zone for a domain."""
        response = await self._request("GET", f"/dns/{domain}")
        return DnsZone(
            domain=response["domain"],
            records=[self._parse_dns_record(r) for r in response.get("records", [])],
            updated_at=response["updated_at"],
        )

    async def set_dns_records(self, domain: str, records: List[DnsRecord]) -> DnsZone:
        """Set DNS records for a domain."""
        body = {"records": [self._dns_record_to_dict(r) for r in records]}
        response = await self._request("PUT", f"/dns/{domain}", body=body)
        return DnsZone(
            domain=response["domain"],
            records=[self._parse_dns_record(r) for r in response.get("records", [])],
            updated_at=response["updated_at"],
        )

    async def add_dns_record(self, domain: str, record: DnsRecord) -> DnsZone:
        """Add a DNS record."""
        body = self._dns_record_to_dict(record)
        response = await self._request("POST", f"/dns/{domain}/records", body=body)
        return DnsZone(
            domain=response["domain"],
            records=[self._parse_dns_record(r) for r in response.get("records", [])],
            updated_at=response["updated_at"],
        )

    async def delete_dns_record(self, domain: str, record_type: str, name: str) -> DnsZone:
        """Delete a DNS record."""
        response = await self._request("DELETE", f"/dns/{domain}/records/{record_type}/{name}")
        return DnsZone(
            domain=response["domain"],
            records=[self._parse_dns_record(r) for r in response.get("records", [])],
            updated_at=response["updated_at"],
        )

    # ==================== Deployment Operations ====================

    async def deploy(
        self,
        cid: str,
        domain: Optional[str] = None,
        subdomain: Optional[str] = None,
        headers: Optional[Dict[str, str]] = None,
        redirects: Optional[List[RedirectRule]] = None,
        spa: bool = False,
        clean_urls: bool = True,
        trailing_slash: bool = False,
    ) -> Deployment:
        """Deploy a site from CID."""
        body: Dict[str, Any] = {"cid": cid}
        if domain:
            body["domain"] = domain
        if subdomain:
            body["subdomain"] = subdomain
        if headers:
            body["headers"] = headers
        if redirects:
            body["redirects"] = [
                {"source": r.source, "destination": r.destination, "status_code": r.status_code}
                for r in redirects
            ]
        body["spa"] = spa
        body["clean_urls"] = clean_urls
        body["trailing_slash"] = trailing_slash

        response = await self._request("POST", "/deployments", body=body)
        return self._parse_deployment(response)

    async def get_deployment(self, deployment_id: str) -> Deployment:
        """Get deployment by ID."""
        response = await self._request("GET", f"/deployments/{deployment_id}")
        return self._parse_deployment(response)

    async def list_deployments(self, domain: Optional[str] = None) -> List[Deployment]:
        """List deployments."""
        path = f"/deployments?domain={domain}" if domain else "/deployments"
        response = await self._request("GET", path)
        return [self._parse_deployment(d) for d in response.get("deployments", [])]

    async def rollback(self, domain: str, deployment_id: str) -> Deployment:
        """Rollback to a previous deployment."""
        response = await self._request(
            "POST", f"/deployments/{deployment_id}/rollback", body={"domain": domain}
        )
        return self._parse_deployment(response)

    async def delete_deployment(self, deployment_id: str) -> None:
        """Delete a deployment."""
        await self._request("DELETE", f"/deployments/{deployment_id}")

    async def get_deployment_stats(self, deployment_id: str, period: str = "24h") -> DeploymentStats:
        """Get deployment stats."""
        response = await self._request("GET", f"/deployments/{deployment_id}/stats?period={period}")
        return DeploymentStats(
            requests=response["requests"],
            bandwidth=response["bandwidth"],
            unique_visitors=response["unique_visitors"],
            period=response["period"],
        )

    # ==================== SSL Operations ====================

    async def provision_ssl(
        self,
        domain: str,
        include_www: bool = True,
        auto_renew: bool = True,
    ) -> Certificate:
        """Provision SSL certificate."""
        body = {"include_www": include_www, "auto_renew": auto_renew}
        response = await self._request("POST", f"/ssl/{domain}", body=body)
        return self._parse_certificate(response)

    async def get_certificate(self, domain: str) -> Certificate:
        """Get certificate status."""
        response = await self._request("GET", f"/ssl/{domain}")
        return self._parse_certificate(response)

    async def renew_certificate(self, domain: str) -> Certificate:
        """Renew SSL certificate."""
        response = await self._request("POST", f"/ssl/{domain}/renew")
        return self._parse_certificate(response)

    async def delete_certificate(self, domain: str) -> None:
        """Delete/revoke SSL certificate."""
        await self._request("DELETE", f"/ssl/{domain}")

    # ==================== Site Configuration ====================

    async def get_site_config(self, domain: str) -> SiteConfig:
        """Get site configuration."""
        response = await self._request("GET", f"/sites/{domain}/config")
        return self._parse_site_config(response)

    async def update_site_config(self, domain: str, **kwargs) -> SiteConfig:
        """Update site configuration."""
        response = await self._request("PATCH", f"/sites/{domain}/config", body=kwargs)
        return self._parse_site_config(response)

    async def purge_cache(self, domain: str, paths: Optional[List[str]] = None) -> int:
        """Purge CDN cache."""
        body = {"paths": paths} if paths else None
        response = await self._request("DELETE", f"/sites/{domain}/cache", body=body)
        return response.get("purged", 0)

    # ==================== Analytics ====================

    async def get_analytics(
        self,
        domain: str,
        period: str = "24h",
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
    ) -> AnalyticsData:
        """Get site analytics."""
        params = {"period": period}
        if start_date:
            params["start"] = start_date
        if end_date:
            params["end"] = end_date

        response = await self._request("GET", f"/sites/{domain}/analytics?{urlencode(params)}")
        return AnalyticsData(
            domain=response["domain"],
            period=response["period"],
            page_views=response["page_views"],
            unique_visitors=response["unique_visitors"],
            bandwidth=response["bandwidth"],
            top_pages=[PageView(path=p["path"], views=p["views"]) for p in response.get("top_pages", [])],
            top_referrers=[Referrer(referrer=r["referrer"], count=r["count"]) for r in response.get("top_referrers", [])],
            top_countries=[Country(country=c["country"], count=c["count"]) for c in response.get("top_countries", [])],
        )

    # ==================== Lifecycle ====================

    async def close(self) -> None:
        """Close the client."""
        if not self._closed:
            await self._client.aclose()
            self._closed = True

    @property
    def is_closed(self) -> bool:
        """Check if the client is closed."""
        return self._closed

    async def __aenter__(self) -> "SynorHosting":
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
        await self.close()

    # ==================== Internal Methods ====================

    async def _request(
        self,
        method: str,
        path: str,
        body: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """Make an HTTP request with retry logic."""
        if self._closed:
            raise HostingError("Client has been closed")

        last_error: Optional[Exception] = None

        for attempt in range(self._config.retries):
            try:
                if method == "GET":
                    response = await self._client.get(path)
                elif method == "POST":
                    response = await self._client.post(path, json=body)
                elif method == "PUT":
                    response = await self._client.put(path, json=body)
                elif method == "PATCH":
                    response = await self._client.patch(path, json=body)
                elif method == "DELETE":
                    if body:
                        response = await self._client.request("DELETE", path, json=body)
                    else:
                        response = await self._client.delete(path)
                else:
                    raise ValueError(f"Unknown method: {method}")

                if response.status_code >= 400:
                    error_data = response.json() if response.content else {}
                    raise HostingError(
                        error_data.get("message") or error_data.get("error") or f"HTTP {response.status_code}",
                        error_data.get("code"),
                        response.status_code,
                    )

                return response.json() if response.content else {}

            except Exception as e:
                last_error = e
                if self._config.debug:
                    print(f"Attempt {attempt + 1} failed: {e}")

                if attempt < self._config.retries - 1:
                    import asyncio
                    await asyncio.sleep(attempt + 1)

        if last_error:
            raise last_error
        raise HostingError("Unknown error after retries")

    def _parse_domain(self, data: Dict[str, Any]) -> Domain:
        records = None
        if "records" in data and data["records"]:
            records = self._parse_domain_record(data["records"])

        return Domain(
            name=data["name"],
            status=DomainStatus(data["status"]),
            owner=data["owner"],
            registered_at=data["registered_at"],
            expires_at=data["expires_at"],
            auto_renew=data.get("auto_renew", True),
            records=records,
        )

    def _parse_domain_record(self, data: Dict[str, Any]) -> DomainRecord:
        return DomainRecord(
            cid=data.get("cid"),
            ipv4=data.get("ipv4"),
            ipv6=data.get("ipv6"),
            cname=data.get("cname"),
            txt=data.get("txt"),
            metadata=data.get("metadata"),
        )

    def _domain_record_to_dict(self, record: DomainRecord) -> Dict[str, Any]:
        result: Dict[str, Any] = {}
        if record.cid:
            result["cid"] = record.cid
        if record.ipv4:
            result["ipv4"] = record.ipv4
        if record.ipv6:
            result["ipv6"] = record.ipv6
        if record.cname:
            result["cname"] = record.cname
        if record.txt:
            result["txt"] = record.txt
        if record.metadata:
            result["metadata"] = record.metadata
        return result

    def _parse_dns_record(self, data: Dict[str, Any]) -> DnsRecord:
        return DnsRecord(
            type=DnsRecordType(data["type"]),
            name=data["name"],
            value=data["value"],
            ttl=data.get("ttl", 3600),
            priority=data.get("priority"),
        )

    def _dns_record_to_dict(self, record: DnsRecord) -> Dict[str, Any]:
        result = {
            "type": record.type.value,
            "name": record.name,
            "value": record.value,
            "ttl": record.ttl,
        }
        if record.priority is not None:
            result["priority"] = record.priority
        return result

    def _parse_deployment(self, data: Dict[str, Any]) -> Deployment:
        return Deployment(
            id=data["id"],
            domain=data["domain"],
            cid=data["cid"],
            status=DeploymentStatus(data["status"]),
            url=data["url"],
            created_at=data["created_at"],
            updated_at=data["updated_at"],
            build_logs=data.get("build_logs"),
            error_message=data.get("error_message"),
        )

    def _parse_certificate(self, data: Dict[str, Any]) -> Certificate:
        return Certificate(
            domain=data["domain"],
            status=CertificateStatus(data["status"]),
            auto_renew=data.get("auto_renew", True),
            issuer=data.get("issuer", ""),
            issued_at=data.get("issued_at"),
            expires_at=data.get("expires_at"),
            fingerprint=data.get("fingerprint"),
        )

    def _parse_site_config(self, data: Dict[str, Any]) -> SiteConfig:
        redirects = None
        if "redirects" in data and data["redirects"]:
            redirects = [
                RedirectRule(
                    source=r["source"],
                    destination=r["destination"],
                    status_code=r.get("status_code", 301),
                    permanent=r.get("permanent", True),
                )
                for r in data["redirects"]
            ]

        return SiteConfig(
            domain=data["domain"],
            cid=data.get("cid"),
            headers=data.get("headers"),
            redirects=redirects,
            error_pages=data.get("error_pages"),
            spa=data.get("spa", False),
            clean_urls=data.get("clean_urls", True),
            trailing_slash=data.get("trailing_slash", False),
        )
