import argparse
import os
import sys
import yaml
import re
import json

def parse_env_file(path):
    """Liest eine .env Datei in ein Dictionary ein."""
    if not os.path.exists(path):
        return {}
    data = {}
    env_regex = re.compile(r'^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$')
    with open(path, 'r', encoding='utf-8') as f:
        for line in f:
            match = env_regex.search(line)
            if match:
                key, val = match.groups()
                # Quotes entfernen
                if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
                    val = val[1:-1]
                data[key] = val
    return data

def write_env_file(path, lines):
    try:
        with open(path, "w", encoding='utf-8') as f:
            f.write("\n".join(lines))
            f.write("\n")
        print(f"✅ Successfully wrote {path}")
    except Exception as e:
        print(f"❌ Error writing file: {e}")
        sys.exit(1)

def generate_env(env_name, config_path="project.yml", output_path=".env"):
    print(f"⚙️  Generating .env for environment: {env_name}")
    
    if not os.path.exists(config_path):
        # Fallback versuchen (falls yaml statt yml)
        if config_path.endswith(".yml") and os.path.exists(config_path.replace(".yml", ".yaml")):
            config_path = config_path.replace(".yml", ".yaml")
        elif config_path.endswith(".yaml") and os.path.exists(config_path.replace(".yaml", ".yml")):
            config_path = config_path.replace(".yaml", ".yml")
        else:
            print(f"❌ Error: Config file '{config_path}' not found.")
            sys.exit(1)

    with open(config_path, "r") as f:
        config = yaml.safe_load(f)

    # 1. Input-Quellen laden (Die "Zutaten")
    # ---------------------------------------------------------
    inputs = {}

    # A) OS Environment (niedrigste Prio)
    # B) Lokale .env.local
    if os.path.exists(".env.local"):
        print("   📂 Loading .env.local")
        inputs.update(parse_env_file(".env.local"))

    # C) GitHub Secrets Context
    secrets_json = os.environ.get("SECRETS_CONTEXT")
    if secrets_json:
        try:
            print("   🔓 Loading SECRETS_CONTEXT from GitHub")
            github_secrets = json.loads(secrets_json)
            inputs.update(github_secrets)
        except json.JSONDecodeError:
            print("   ⚠️ Error decoding SECRETS_CONTEXT")

    if env_name == "edge" and os.path.exists(".env.edge"):
        print("   📂 Loading .env.edge (Edge Overrides)")
        edge_vars = parse_env_file(".env.edge")
        inputs.update(edge_vars)

    # 2. Struktur-Daten aus project.yml laden
    # ---------------------------------------------------------
    if env_name not in config.get("environments", {}):
        print(f"❌ Error: Environment '{env_name}' not defined in {config_path}")
        sys.exit(1)

    env_config = config["environments"][env_name]
    domains = env_config.get("domains", [])
    use_traefik = env_config.get("use_traefik", False)
    
    # NEU: Ports aus der Config laden (mit Defaults)
    web_port = env_config.get("web_port", 8000)
    frontend_port = env_config.get("frontend_port", 5173)

    overrides = env_config.get("env_overrides", {})
    if overrides:
        print(f"   ⚡ Applying {len(overrides)} overrides from project.yml")
        inputs.update(overrides)
    
    # Prefix Logik (aus project.yml container_prefix oder default 'app')
    base_prefix = config.get("container_prefix", "app")
    if env_name == "staging":
        ctr_prefix = f"{base_prefix}_stage"
    elif env_name == "production":
        ctr_prefix = f"{base_prefix}_prod"
    elif env_name == "edge":
        ctr_prefix = f"{base_prefix}_edge"
    else:
        # Lokal nutzen wir oft auch einen Prefix, z.B. "hram_local"
        # Falls du lokal keinen Suffix willst, nimm nur base_prefix
        ctr_prefix = f"{base_prefix}_{env_name}"

    # 3. .env Zusammenbauen
    # ---------------------------------------------------------
    lines = []
    # Header Info
    lines.append(f"# Auto-generated for environment: {env_name}")
    lines.append(f"# Config Source: {config_path}")
    
    written_keys = set()

    def add(key, value):
        """Helper: Fügt Zeile hinzu, verhindert Duplikate."""
        if key not in written_keys:
            lines.append(f"{key}={value}")
            written_keys.add(key)
    
    # --- A. Infrastruktur & Meta (Berechnet) ---
    add("ENV_TYPE", env_name)
    add("PROJECT_NAME", config.get("project_name", "Project"))
    add("COMPOSE_PROJECT_NAME", f"{config.get('project_name')}_{env_name}")
    add("CONTAINER_NAME_PREFIX", ctr_prefix)
    add("IMAGE_TAG", "latest") # Default, wird oft von CI überschrieben
    
    # Ports explizit setzen (wichtig für Docker Compose Variable Substitution)
    add("WEB_PORT", str(web_port))
    add("FRONTEND_PORT", str(frontend_port))

    add("ROUTER_NAME", f"{config.get('project_name')}-{env_name}")
    add("MFA_WEBAUTHN_RP_NAME", config.get("project_name"))
    
    # Traefik & Backup
    add("BACKUP_ENABLE", "true" if env_name == "production" else "false")
    add("TRAEFIK_ENABLE", "true" if use_traefik else "false")
    # External Proxy Logik für Traefik Network
    add("USE_EXTERNAL_PROXY", "true" if (use_traefik and env_name in ("staging", "production")) else "false")
    
    # Volumes (Namen berechnen)
    vol_config = env_config.get("volumes", {})
    
    def get_vol_name(key, default_suffix):
        # Prüft ob Dict oder String in der Config steht
        val = vol_config.get(key)
        if isinstance(val, dict):
            return val.get("name", f"{ctr_prefix}_{default_suffix}")
        return val if val else f"{ctr_prefix}_{default_suffix}"

    add("DB_VOLUME_NAME", get_vol_name("postgres_data", "postgres_data"))
    add("MEDIA_VOLUME_NAME", get_vol_name("media_volume", "media_volume"))
    add("EXCEL_VOLUME_NAME", get_vol_name("excel_volume", "excel_volume"))

    # --- B. Netzwerk & URLs (Berechnet + Inputs) ---
    
    # Master Config
    if domains:
        add("MASTER_BASE_URL", f"https://{domains[0]}")
    
    if "master_public_ip" in env_config:
        add("MASTER_PUBLIC_IP", env_config["master_public_ip"])

    # Allowed Hosts
    # Immer Localhost und interne Docker-Namen erlauben
    host_list = list(domains)
    host_list.extend(["localhost", "127.0.0.1", "backend", f"{ctr_prefix}_backend"])
    add("DJANGO_ALLOWED_HOSTS", ",".join(host_list))
    
    # CSRF Trusted Origins & Public Origin
    protocol = "https" if use_traefik else "http"
    csrf_urls = [f"{protocol}://{d}" for d in domains]
    
    if env_name == "local":
        # NEU: Hier kommt die Magie für deine Hybrid-Entwicklung
        # Wir vertrauen dem Frontend (Vite) UND dem Backend
        
        # 1. Frontend (Vite) - Da läuft die UI
        csrf_urls.append(f"http://localhost:{frontend_port}")
        csrf_urls.append(f"http://127.0.0.1:{frontend_port}")
        
        # 2. Backend (Django) - Da laufen API-Calls direkt hin
        csrf_urls.append(f"http://localhost:{web_port}")
        csrf_urls.append(f"http://127.0.0.1:{web_port}")
        
        # Public Origin ist lokal das Frontend!
        add("PUBLIC_ORIGIN", f"http://localhost:{frontend_port}")
        
        # Debug immer an lokal
        add("DEBUG", "False")
    else:
        # Server Environment
        main_domain = domains[0] if domains else "localhost"
        add("PUBLIC_ORIGIN", f"{protocol}://{main_domain}")
        # Debug aus Inputs nehmen oder False
        if "DEBUG" not in inputs:
            add("DEBUG", "False")

    add("CSRF_TRUSTED_URLS", ",".join(csrf_urls))
    
    # Traefik Rules
    if use_traefik and domains:
        rules = [f"Host(`{d}`)" for d in domains]
        add("TRAEFIK_ROUTER_RULE", " || ".join(rules))
    else:
        # Fallback lokal
        add("TRAEFIK_ROUTER_RULE", f"Host(`localhost`)")


    # --- C. Der "Dumme" Teil: Alles aus Inputs übernehmen ---
    print(f"   📥 Injecting {len(inputs)} variables from inputs...")
    
    for key in sorted(inputs.keys()):
        # Überspringe interne GitHub-Keys, leere Keys und SSH_ Keys
        if not key or key.startswith("GITHUB_") or key.startswith("SSH_"): 
            continue
            
        val = inputs[key]
        
        # Multiline Handling
        if isinstance(val, str) and "\n" in val:
            # FIX: Backslashes sind in f-strings < Python 3.12 problematisch
            clean_val = val.replace(chr(10), "\\n").replace(chr(13), "")
            val = f'"{clean_val}"'

        add(key, val)

    # 4. Schreiben
    write_env_file(output_path, lines)

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--env", required=True, help="Environment (production, staging, local, edge)")
    parser.add_argument("--output", default=".env", help="Output file path")
    parser.add_argument("--config", default="project.yml", help="Path to project.yml")
    args = parser.parse_args()
    
    generate_env(args.env, config_path=args.config, output_path=args.output)

if __name__ == "__main__":
    main()