"""ArgoCD API client wrapper."""

import os
import sys
import json
import httpx
from typing import Optional, Dict, Any

def get_env_file_path() -> str:
    """Get the path to the .env file."""
    home = os.path.expanduser("~")
    ngen_dir = os.path.join(home, ".ngen-argoapi")
    return os.path.join(ngen_dir, ".env")

def load_env_file() -> dict:
    """Load environment variables from ~/.ngen-argoapi/.env file."""
    env_file = get_env_file_path()
    env_vars = {}

    if os.path.exists(env_file):
        try:
            with open(env_file, 'r') as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith('#'):
                        if '=' in line:
                            key, value = line.split('=', 1)
                            env_vars[key.strip()] = value.strip()
        except Exception as e:
            print(f"Warning: Could not read .env file: {e}", file=sys.stderr)

    return env_vars

def save_env_file(env_vars: dict) -> bool:
    """Save environment variables to ~/.ngen-argoapi/.env file."""
    env_file = get_env_file_path()

    try:
        # Create directory if it doesn't exist
        os.makedirs(os.path.dirname(env_file), exist_ok=True)

        with open(env_file, 'w') as f:
            f.write("# ngen-argoapi ArgoCD credentials\n")
            f.write("# Generated by: argoapi login\n")
            f.write("\n")
            for key, value in env_vars.items():
                f.write(f"{key}={value}\n")

        # Set restrictive permissions (readable only by owner)
        os.chmod(env_file, 0o600)
        return True
    except Exception as e:
        print(f"Error: Could not save .env file: {e}", file=sys.stderr)
        return False

class ArgocdClient:
    """ArgoCD API client wrapper."""

    def __init__(self):
        """Initialize ArgoCD client from environment variables."""
        # Load from .env file first, then override with current env vars
        env_vars = load_env_file()
        self.url = os.getenv("ARGOCD_URL") or env_vars.get("ARGOCD_URL")
        self.token = os.getenv("ARGOCD_TOKEN") or env_vars.get("ARGOCD_TOKEN")
        self.insecure = True # Default to insecure as requested

        if not self.url:
            print("Error: ARGOCD_URL environment variable is required", file=sys.stderr)
            print("Use 'argoapi login' to set credentials", file=sys.stderr)
            sys.exit(1)
        
        # Strip trailing slash from URL
        if self.url.endswith('/'):
            self.url = self.url[:-1]

        if not self.token:
             print("Error: ARGOCD_TOKEN is required. Use 'argoapi login' to authenticate.", file=sys.stderr)
             sys.exit(1)

    def _get_headers(self) -> dict:
        return {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json"
        }

    def login(self, username, password) -> str:
        """
        Login to ArgoCD and get a session token.
        Returns the token.
        """
        login_url = f"{self.url}/api/v1/session"
        payload = {
            "username": username,
            "password": password
        }
        
        try:
            response = httpx.post(login_url, json=payload, verify=not self.insecure)
            response.raise_for_status()
            data = response.json()
            return data.get("token")
        except Exception as e:
             raise Exception(f"Login failed: {e}")

    def get_application(self, app_name: str) -> Dict[str, Any]:
        """Get application details."""
        api_url = f"{self.url}/api/v1/applications/{app_name}"
        try:
            response = httpx.get(api_url, headers=self._get_headers(), verify=not self.insecure)
            response.raise_for_status()
            return response.json()
        except httpx.HTTPStatusError as e:
            if e.response.status_code == 404:
                raise KeyError(f"Application '{app_name}' not found")
            raise Exception(f"Error getting application: {e}")
        except Exception as e:
            raise Exception(f"Error requesting application: {e}")

    def refresh_application(self, app_name: str, hard: bool = False) -> Dict[str, Any]:
        """
        Refresh (invalidate cache) for an application.
        Using 'refresh' query param.
        hard=True -> refresh=hard
        hard=False -> refresh=normal
        """
        refresh_type = "hard" if hard else "normal"
        api_url = f"{self.url}/api/v1/applications/{app_name}?refresh={refresh_type}"
        
        try:
            # For refresh, we generally use GET according to some docs, 
            # but sometimes it's driven by GET with refresh param which triggers the refresh.
            # ArgoCD API: GET /api/v1/applications/{name}?refresh=true
            response = httpx.get(api_url, headers=self._get_headers(), verify=not self.insecure)
            response.raise_for_status()
            return response.json()
        except Exception as e:
             raise Exception(f"Error refreshing application: {e}")

    def list_applications(self) -> list:
        """List all applications."""
        api_url = f"{self.url}/api/v1/applications"
        try:
            response = httpx.get(api_url, headers=self._get_headers(), verify=not self.insecure)
            response.raise_for_status()
            data = response.json()
            return data.get("items", [])
        except Exception as e:
             raise Exception(f"Error listing applications: {e}")

    def get_managed_resources(self, app_name: str) -> list:
        """Get managed resources for an application."""
        api_url = f"{self.url}/api/v1/applications/{app_name}/managed-resources"
        try:
            response = httpx.get(api_url, headers=self._get_headers(), verify=not self.insecure)
            response.raise_for_status()
            data = response.json()
            return data.get("items", [])
        except Exception as e:
            raise Exception(f"Error getting managed resources: {e}")

    def get_resource_diff(self, app_name: str, group: str, kind: str, name: str, namespace: str, version: str) -> dict:
        """Get diff for a specific resource."""
        # Query params: group, kind, name, namespace, version
        params = {
            "group": group,
            "kind": kind,
            "name": name,
            "namespace": namespace,
            "version": version
        }
        # Filter out empty params (e.g. group can be empty)
        params = {k: v for k, v in params.items() if v is not None}
        
        api_url = f"{self.url}/api/v1/applications/{app_name}/resource/diff"
        try:
            response = httpx.get(api_url, params=params, headers=self._get_headers(), verify=not self.insecure)
            # 404 might mean no diff or resource not found, but API usually returns diff or empty
            if response.status_code == 404:
                return {}
            response.raise_for_status()
            return response.json()
        except Exception as e:
            # Some resources might fail to diff, we'll return empty or log error
            # For now, propagate exception but maybe handle 500s gracefully?
            # Let's just return minimal error info if we can't get diff
            print(f"Warning: Failed to get diff for {kind}/{name}: {e}", file=sys.stderr)
            return {}

    @staticmethod
    def obtain_token(url: str, username: str, password: str, insecure: bool = True) -> str:
        """Static method to obtain token without initializing client fully."""
        if url.endswith('/'):
            url = url[:-1]
        login_url = f"{url}/api/v1/session"
        payload = {
            "username": username,
            "password": password
        }
        try:
            response = httpx.post(login_url, json=payload, verify=not insecure)
            response.raise_for_status()
            data = response.json()
            return data.get("token")
        except Exception as e:
             # Try getting error message from response
             if isinstance(e, httpx.HTTPStatusError):
                 try:
                    err_data = e.response.json()
                    error_msg = err_data.get('error', str(e))
                    raise Exception(f"Login failed: {error_msg}")
                 except:
                     pass
             raise Exception(f"Login failed: {e}")
