"""
Cloudflare API integration for DNS management
"""

import requests
import json
from typing import Dict, List, Optional
from .config import Config


class CloudflareManager:
    """Manages Cloudflare DNS records"""

    def __init__(self, token_name: Optional[str] = None):
        """
        Initialize CloudflareManager

        Args:
            token_name: Optional name of a specific token to use.
                      If not provided, uses default CLOUDFLARE_API_TOKEN
        """
        if token_name:
            self.api_token = Config.get_cloudflare_token(token_name)
        else:
            self.api_token = Config.get_env("CLOUDFLARE_API_TOKEN")

        self.account_id = Config.get_env("CLOUDFLARE_ACCOUNT_ID")
        self.zone_id = Config.get_env("CLOUDFLARE_ZONE_ID")
        self.base_url = "https://api.cloudflare.com/client/v4"
        self.token_name = token_name

    def _headers(self) -> Dict[str, str]:
        """Get headers for API requests"""
        return {
            "Authorization": f"Bearer {self.api_token}",
            "Content-Type": "application/json"
        }

    def test_connection(self) -> Dict:
        """Test connection to Cloudflare API"""
        if not self.api_token:
            return {
                "success": False,
                "error": "No API token configured"
            }

        try:
            response = requests.get(
                f"{self.base_url}/user/tokens/verify",
                headers=self._headers()
            )
            return {
                "success": response.json().get("success", False),
                "data": response.json()
            }
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }

    def list_zones(self) -> List[Dict]:
        """List all domains/zones"""
        if not self.api_token:
            return []

        try:
            response = requests.get(
                f"{self.base_url}/zones",
                headers=self._headers()
            )
            data = response.json()
            if data.get("success"):
                return data.get("result", [])
            return []
        except Exception as e:
            print(f"Error listing zones: {e}")
            return []

    def list_dns_records(self, zone_id: str) -> List[Dict]:
        """List DNS records for a zone"""
        if not self.api_token:
            return []

        try:
            response = requests.get(
                f"{self.base_url}/zones/{zone_id}/dns_records",
                headers=self._headers()
            )
            data = response.json()
            if data.get("success"):
                return data.get("result", [])
            return []
        except Exception as e:
            print(f"Error listing DNS records: {e}")
            return []

    def add_dns_record(self, zone_id: str, record_type: str, name: str,
                       content: str, ttl: int = 1, proxied: bool = True) -> Dict:
        """Add a new DNS record"""
        if not self.api_token:
            return {"success": False, "error": "No API token configured"}

        try:
            data = {
                "type": record_type,
                "name": name,
                "content": content,
                "ttl": ttl,
                "proxied": proxied
            }
            response = requests.post(
                f"{self.base_url}/zones/{zone_id}/dns_records",
                headers=self._headers(),
                json=data
            )
            return response.json()
        except Exception as e:
            return {"success": False, "error": str(e)}

    def delete_dns_record(self, zone_id: str, record_id: str) -> Dict:
        """Delete a DNS record"""
        if not self.api_token:
            return {"success": False, "error": "No API token configured"}

        try:
            response = requests.delete(
                f"{self.base_url}/zones/{zone_id}/dns_records/{record_id}",
                headers=self._headers()
            )
            return response.json()
        except Exception as e:
            return {"success": False, "error": str(e)}

    def update_dns_record(self, zone_id: str, record_id: str, record_type: str,
                          name: str, content: str, ttl: int = 1, proxied: bool = True) -> Dict:
        """Update an existing DNS record"""
        if not self.api_token:
            return {"success": False, "error": "No API token configured"}

        try:
            data = {
                "type": record_type,
                "name": name,
                "content": content,
                "ttl": ttl,
                "proxied": proxied
            }
            response = requests.put(
                f"{self.base_url}/zones/{zone_id}/dns_records/{record_id}",
                headers=self._headers(),
                json=data
            )
            return response.json()
        except Exception as e:
            return {"success": False, "error": str(e)}

    def find_dns_record(self, zone_id: str, name: str, record_type: str = "A") -> Optional[Dict]:
        """Find a DNS record by name and type"""
        records = self.list_dns_records(zone_id)
        for record in records:
            if record.get("name") == name and record.get("type") == record_type:
                return record
        return None

    def ensure_dns_record(self, zone_id: str, record_type: str, name: str,
                          content: str, ttl: int = 1, proxied: bool = True) -> Dict:
        """Create or update a DNS record

        Args:
            zone_id: Cloudflare zone ID
            record_type: Record type (A, CNAME, etc.)
            name: Full domain name (e.g., app.example.com)
            content: Record content (IP for A, target for CNAME)
            ttl: TTL in seconds (1 = auto)
            proxied: Whether to proxy through Cloudflare

        Returns:
            Dict with success status, action taken, and record data
        """
        existing = self.find_dns_record(zone_id, name, record_type)

        if existing:
            # Check if update needed
            if existing.get("content") == content and existing.get("proxied") == proxied:
                return {
                    "success": True,
                    "action": "unchanged",
                    "message": f"Record {name} already exists with correct value",
                    "record": existing
                }

            # Update existing record
            result = self.update_dns_record(
                zone_id, existing["id"], record_type, name, content, ttl, proxied
            )
            if result.get("success"):
                return {
                    "success": True,
                    "action": "updated",
                    "message": f"Updated {name} -> {content}",
                    "record": result.get("result")
                }
            else:
                return {
                    "success": False,
                    "action": "update_failed",
                    "message": result.get("errors", [{"message": "Unknown error"}])[0].get("message"),
                    "error": result
                }
        else:
            # Create new record
            result = self.add_dns_record(zone_id, record_type, name, content, ttl, proxied)
            if result.get("success"):
                return {
                    "success": True,
                    "action": "created",
                    "message": f"Created {name} -> {content}",
                    "record": result.get("result")
                }
            else:
                return {
                    "success": False,
                    "action": "create_failed",
                    "message": result.get("errors", [{"message": "Unknown error"}])[0].get("message"),
                    "error": result
                }

    @staticmethod
    def get_public_ip() -> Optional[str]:
        """Get the server's public IP address"""
        try:
            # Try multiple services in case one is down
            services = [
                "https://api.ipify.org",
                "https://ifconfig.me/ip",
                "https://icanhazip.com",
            ]
            for url in services:
                try:
                    response = requests.get(url, timeout=5)
                    if response.status_code == 200:
                        return response.text.strip()
                except:
                    continue
            return None
        except:
            return None

    def get_zone_by_name(self, domain: str) -> Optional[Dict]:
        """Get a zone by domain name"""
        zones = self.list_zones()
        for zone in zones:
            if zone["name"] == domain:
                return zone
        return None

    @staticmethod
    def extract_root_domain(fqdn: str) -> str:
        """Extract root domain from FQDN (e.g., app.example.com -> example.com)"""
        parts = fqdn.rstrip('.').split('.')
        if len(parts) >= 2:
            return '.'.join(parts[-2:])
        return fqdn

    @staticmethod
    def find_credential_for_domain(domain: str) -> Optional[Dict]:
        """
        Find which stored credential can manage a given domain.

        Args:
            domain: Full domain (e.g., app.example.com) or root domain (example.com)

        Returns:
            Dict with 'name', 'token', and 'zone' if found, None otherwise
        """
        root_domain = CloudflareManager.extract_root_domain(domain)
        tokens = Config.get_cloudflare_tokens()

        for name, data in tokens.items():
            token = data.get("token")
            if not token:
                continue

            # Create a temporary manager to query zones
            manager = CloudflareManager.__new__(CloudflareManager)
            manager.api_token = token
            manager.base_url = "https://api.cloudflare.com/client/v4"

            zones = manager.list_zones()
            for zone in zones:
                if zone.get("name") == root_domain:
                    return {
                        "credential_name": name,
                        "token": token,
                        "zone": zone
                    }

        return None

    @staticmethod
    def setup_dns_for_domain(domain: str, ip: Optional[str] = None, proxied: bool = True) -> Dict:
        """
        Set up DNS for a domain using stored credentials.

        Automatically finds the right credential, gets public IP if needed,
        and creates or updates the A record.

        Args:
            domain: Full domain name (e.g., app.example.com)
            ip: IP address to point to (defaults to server's public IP)
            proxied: Whether to proxy through Cloudflare (default True)

        Returns:
            Dict with success status and details
        """
        # Find credential that can manage this domain
        cred = CloudflareManager.find_credential_for_domain(domain)
        if not cred:
            return {
                "success": False,
                "message": f"No Cloudflare credential found that can manage {domain}"
            }

        # Get IP address
        if not ip:
            ip = CloudflareManager.get_public_ip()
            if not ip:
                return {
                    "success": False,
                    "message": "Could not determine server's public IP address"
                }

        # Create manager with the found credential
        manager = CloudflareManager(cred["credential_name"])
        zone_id = cred["zone"]["id"]

        # Ensure the DNS record exists
        result = manager.ensure_dns_record(zone_id, "A", domain, ip, proxied=proxied)

        if result["success"]:
            result["credential_used"] = cred["credential_name"]
            result["ip"] = ip

        return result

    @staticmethod
    def get_all_zones_from_all_credentials() -> List[Dict]:
        """
        Get all zones from all stored credentials.

        Returns:
            List of dicts with zone info and credential name
        """
        all_zones = []
        tokens = Config.get_cloudflare_tokens()

        for name, data in tokens.items():
            token = data.get("token")
            if not token:
                continue

            manager = CloudflareManager.__new__(CloudflareManager)
            manager.api_token = token
            manager.base_url = "https://api.cloudflare.com/client/v4"

            zones = manager.list_zones()
            for zone in zones:
                all_zones.append({
                    "credential_name": name,
                    "zone_id": zone.get("id"),
                    "zone_name": zone.get("name"),
                    "status": zone.get("status")
                })

        return all_zones
