import sys
import json
import urllib.request
import urllib.parse
import os

HA_CONFIG_ENTRIES_PATH = '/home/jesus/homeassistant/.storage/core.config_entries'
HA_STATUS_JSON_PATH = '/home/jesus/homeassistant/cf_status.json'
HA_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMjlhZDk5OGU5ZjE0NWRmYWY2NDRjMzY5ZGQyMjBiYyIsImlhdCI6MTY5MzgyNDk2MiwiZXhwIjoyMDA5MTg0OTYyfQ.JQJLsqxkNx_mM70t2vssGRGZiBqDrN4I_G89WBdtwB4"
HA_BASE_URL = "http://192.168.1.120:8123"
API_TOKEN = "6iX6sF3lgkFzVrq_8mockxvTZSkY2dOVgTGTGIZy"

def get_ha_cf_entries():
    with open(HA_CONFIG_ENTRIES_PATH, 'r', encoding='utf-8') as f:
        data = json.load(f)
    cf_entries = [e for e in data['data']['entries'] if e.get('domain') == 'cloudflare']
    return data, cf_entries

def save_ha_config_entries(data):
    with open(HA_CONFIG_ENTRIES_PATH, 'w', encoding='utf-8') as f:
        json.dump(data, f, indent=2, ensure_ascii=False)

def call_ha_service(domain, service, service_data):
    try:
        url = f"{HA_BASE_URL}/api/services/{domain}/{service}"
        req = urllib.request.Request(
            url,
            data=json.dumps(service_data).encode('utf-8'),
            headers={
                "Authorization": f"Bearer {HA_TOKEN}",
                "Content-Type": "application/json"
            }
        )
        with urllib.request.urlopen(req, timeout=5) as resp:
            return True
    except Exception as ex:
        print(f"Error calling HA service {domain}.{service}: {ex}")
        return False

def reload_ha_cf_entries(cf_entries):
    for entry in cf_entries:
        try:
            entry_id = entry.get('entry_id')
            url = f"{HA_BASE_URL}/api/config/config_entries/entry/{entry_id}/reload"
            req = urllib.request.Request(
                url,
                data=b"{}",
                headers={
                    "Authorization": f"Bearer {HA_TOKEN}",
                    "Content-Type": "application/json"
                }
            )
            with urllib.request.urlopen(req, timeout=5) as resp:
                pass
        except:
            pass

def get_all_cf_zones():
    headers = {
        "Authorization": f"Bearer {API_TOKEN}",
        "Content-Type": "application/json"
    }
    try:
        req = urllib.request.Request("https://api.cloudflare.com/client/v4/zones", headers=headers)
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode('utf-8'))
            return {z['name']: z['id'] for z in data.get('result', [])}
    except Exception as ex:
        print(f"Error fetching zones: {ex}")
        return {}

def get_cf_dns_records(zone_id):
    headers = {
        "Authorization": f"Bearer {API_TOKEN}",
        "Content-Type": "application/json"
    }
    try:
        req_dns = urllib.request.Request(f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records?per_page=100", headers=headers)
        with urllib.request.urlopen(req_dns, timeout=10) as resp:
            dns_data = json.loads(resp.read().decode('utf-8'))
            return dns_data.get('result', [])
    except Exception as ex:
        print(f"Error fetching DNS records: {ex}")
        return []

def update_cf_record_ip(zone_id, record_id, name, new_ip, proxied):
    headers = {
        "Authorization": f"Bearer {API_TOKEN}",
        "Content-Type": "application/json"
    }
    url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{record_id}"
    payload = json.dumps({
        "type": "A",
        "name": name,
        "content": new_ip,
        "proxied": proxied
    }).encode('utf-8')
    try:
        req = urllib.request.Request(url, data=payload, headers=headers, method='PATCH')
        with urllib.request.urlopen(req, timeout=10) as resp:
            return True
    except Exception as ex:
        print(f"Error updating CF record {name}: {ex}")
        return False

def get_public_ip():
    try:
        with urllib.request.urlopen("https://api.ipify.org", timeout=5) as resp:
            return resp.read().decode('utf-8').strip()
    except:
        return "Unknown"

def sync_all():
    full_data, cf_entries = get_ha_cf_entries()
    zones_map = get_all_cf_zones()
    public_ip = get_public_ip()

    # Ensure all primary active zones exist in HA config_entries
    target_zones = ['jesuslobo.es', 'themeparkcardgame.com']
    existing_entry_zones = [e['data'].get('zone') for e in cf_entries]

    modified = False
    for tz in target_zones:
        if tz not in existing_entry_zones and tz in zones_map:
            import uuid
            initial_recs = ['themeparkcardgame.com', 'www.themeparkcardgame.com'] if tz == 'themeparkcardgame.com' else ['mitunnelkittywireguard.jesuslobo.es', 'cloud.jesuslobo.es']
            new_entry = {
                "entry_id": uuid.uuid4().hex,
                "version": 1,
                "minor_version": 1,
                "domain": "cloudflare",
                "title": tz,
                "data": {
                    "api_token": API_TOKEN,
                    "zone": tz,
                    "records": initial_recs
                },
                "options": {},
                "pref_disable_new_entities": False,
                "pref_disable_polling": False,
                "source": "user",
                "unique_id": None,
                "disabled_by": None
            }
            full_data['data']['entries'].append(new_entry)
            cf_entries.append(new_entry)
            modified = True

    if modified:
        save_ha_config_entries(full_data)

    zones_info = {}
    all_monitored_domains = []
    total_count = 0

    for entry in cf_entries:
        z_name = entry['data'].get('zone')
        z_records = entry['data'].get('records', [])
        z_id = zones_map.get(z_name)
        dns_records = get_cf_dns_records(z_id) if z_id else []

        recs_info = []
        for r_name in z_records:
            all_monitored_domains.append(r_name)
            total_count += 1
            matched = [cr for cr in dns_records if cr['name'] == r_name]
            if matched:
                m = matched[0]
                recs_info.append({
                    "name": r_name,
                    "type": m['type'],
                    "content": m['content'],
                    "proxied": m['proxied']
                })
            else:
                recs_info.append({
                    "name": r_name,
                    "type": "A",
                    "content": "Desconocido",
                    "proxied": False
                })

        zones_info[z_name] = {
            "zone": z_name,
            "records": z_records,
            "records_info": recs_info,
            "all_available": [cr['name'] for cr in dns_records if cr['type'] in ['A', 'CNAME']]
        }

    status_obj = {
        "public_ip": public_ip,
        "count": total_count,
        "zones": list(zones_info.keys()),
        "all_records": all_monitored_domains,
        "zones_info": zones_info
    }

    with open(HA_STATUS_JSON_PATH, 'w', encoding='utf-8') as f:
        json.dump(status_obj, f, indent=2, ensure_ascii=False)

    # Sync options to input_select.cloudflare_dominio_eliminar
    if all_monitored_domains:
        call_ha_service("input_select", "set_options", {
            "entity_id": "input_select.cloudflare_dominio_eliminar",
            "options": all_monitored_domains
        })
    else:
        call_ha_service("input_select", "set_options", {
            "entity_id": "input_select.cloudflare_dominio_eliminar",
            "options": ["(Ninguno)"]
        })

    print(json.dumps(status_obj, indent=2))

def add_domain(domain):
    if not domain or domain.strip() == "":
        return
    domain = domain.strip().lower()
    full_data, cf_entries = get_ha_cf_entries()
    zones_map = get_all_cf_zones()

    # Determine which zone this domain belongs to
    target_zone = None
    for z in zones_map.keys():
        if domain == z or domain.endswith(f".{z}"):
            target_zone = z
            break

    if not target_zone:
        # Default to jesuslobo.es if just a subdomain name was written
        target_zone = "jesuslobo.es"
        domain = f"{domain}.{target_zone}"

    # Find or create entry for target_zone
    matched_entry = None
    for entry in cf_entries:
        if entry['data'].get('zone') == target_zone:
            matched_entry = entry
            break

    if not matched_entry:
        import uuid
        matched_entry = {
            "entry_id": uuid.uuid4().hex,
            "version": 1,
            "minor_version": 1,
            "domain": "cloudflare",
            "title": target_zone,
            "data": {
                "api_token": API_TOKEN,
                "zone": target_zone,
                "records": []
            },
            "options": {},
            "pref_disable_new_entities": False,
            "pref_disable_polling": False,
            "source": "user",
            "unique_id": None,
            "disabled_by": None
        }
        full_data['data']['entries'].append(matched_entry)
        cf_entries.append(matched_entry)

    records = matched_entry['data'].get('records', [])
    if domain not in records:
        records.append(domain)
        matched_entry['data']['records'] = records
        save_ha_config_entries(full_data)
        print(f"Added {domain} to zone {target_zone} in HA.")
        reload_ha_cf_entries([matched_entry])
    else:
        print(f"Domain {domain} was already present in zone {target_zone}.")

    sync_all()

def remove_domain(domain):
    if not domain or domain.strip() == "" or domain == "(Ninguno)":
        return
    domain = domain.strip().lower()
    full_data, cf_entries = get_ha_cf_entries()

    modified = False
    for entry in cf_entries:
        records = entry['data'].get('records', [])
        if domain in records:
            records.remove(domain)
            entry['data']['records'] = records
            modified = True
            print(f"Removed {domain} from zone {entry['data'].get('zone')} in HA.")

    if modified:
        save_ha_config_entries(full_data)
        reload_ha_cf_entries(cf_entries)

    sync_all()

def force_update():
    full_data, cf_entries = get_ha_cf_entries()
    zones_map = get_all_cf_zones()
    public_ip = get_public_ip()

    if public_ip != "Unknown":
        for entry in cf_entries:
            z_name = entry['data'].get('zone')
            z_records = entry['data'].get('records', [])
            z_id = zones_map.get(z_name)
            if z_id:
                dns_records = get_cf_dns_records(z_id)
                for r_name in z_records:
                    matched = [cr for cr in dns_records if cr['name'] == r_name and cr['type'] == 'A']
                    for m in matched:
                        if m['content'] != public_ip:
                            print(f"Updating {r_name} in {z_name} -> {public_ip}")
                            update_cf_record_ip(z_id, m['id'], r_name, public_ip, m['proxied'])
                        else:
                            print(f"{r_name} in {z_name} already has current public IP {public_ip}.")

    # Trigger HA service
    call_ha_service("cloudflare", "update_records", {})
    sync_all()

if __name__ == '__main__':
    if len(sys.argv) < 2 or sys.argv[1] == 'sync':
        sync_all()
    elif sys.argv[1] == 'add' and len(sys.argv) > 2:
        add_domain(sys.argv[2])
    elif sys.argv[1] == 'remove' and len(sys.argv) > 2:
        remove_domain(sys.argv[2])
    elif sys.argv[1] == 'update':
        force_update()
