import json
import random
import time
import xml.etree.ElementTree as ET
from datetime import timedelta

import requests
import frappe
from frappe.utils import getdate

CBAR_URL = "https://www.cbar.az/currencies/{}.xml"
TO_CURRENCY = "AZN"


@frappe.whitelist()
def get_cbar_currencies():
    """Return list of currency codes available on CBAR (Xarici valyutalar only),
    intersected with currencies that exist in ERPNext."""
    from datetime import date as _date

    today = _date.today().strftime("%d.%m.%Y")
    try:
        resp = requests.get(CBAR_URL.format(today), timeout=15)
        resp.raise_for_status()
        root = ET.fromstring(resp.content)
    except Exception as e:
        frappe.log_error(title="CBAR get_currencies failed", message=str(e))
        frappe.throw(frappe._("Could not fetch currency list from CBAR"))

    existing = set(frappe.db.get_all("Currency", filters={"enabled": 1}, pluck="name"))

    result = []
    for val_type in root.findall("ValType"):
        if val_type.get("Type", "") != "Xarici valyutalar":
            continue
        for valute in val_type.findall("Valute"):
            code = valute.get("Code")
            if code and code in existing:
                result.append(code)

    return result


@frappe.whitelist()
def fetch_cbar_rates(from_date, to_date, currencies=None):
    from_date = getdate(from_date)
    to_date   = getdate(to_date)

    if from_date > to_date:
        frappe.throw(frappe._("From Date cannot be after To Date"))

    if isinstance(currencies, str):
        currencies = json.loads(currencies)

    user = frappe.session.user

    frappe.enqueue(
        _import_in_background,
        queue="long",
        timeout=600,
        from_date=from_date,
        to_date=to_date,
        user=user,
        currencies=currencies or [],
    )

    return "enqueued"


def _import_in_background(from_date, to_date, user, currencies):
    created, updated, skipped = 0, 0, []

    dates = []
    d = from_date
    while d <= to_date:
        dates.append(d)
        d += timedelta(days=1)
    total = len(dates)

    # Cache which currencies actually exist in ERPNext
    existing_currencies = set(
        frappe.db.get_all("Currency", filters={"enabled": 1}, pluck="name")
    )

    for idx, current in enumerate(dates):
        date_cbar = current.strftime("%d.%m.%Y")
        date_db   = current.strftime("%Y-%m-%d")

        frappe.publish_realtime(
            "cbar_import_progress",
            {"current": idx + 1, "total": total, "date": date_cbar},
            user=user,
        )

        try:
            resp = requests.get(CBAR_URL.format(date_cbar), timeout=15)
            resp.raise_for_status()
            root = ET.fromstring(resp.content)

            for val_type in root.findall("ValType"):
                for valute in val_type.findall("Valute"):
                    code = valute.get("Code")

                    # Filter by user-selected currencies (empty = all)
                    if currencies and code not in currencies:
                        continue

                    # Skip currencies not in ERPNext
                    if code not in existing_currencies:
                        continue

                    nominal_raw = valute.findtext("Nominal", "1")
                    nominal = int("".join(filter(str.isdigit, nominal_raw)) or "1")
                    value   = float(valute.findtext("Value", "0"))
                    if not nominal or not value:
                        continue

                    rate     = value / nominal
                    doc_name = f"{date_db}-{code}-{TO_CURRENCY}-Selling-Buying"

                    if frappe.db.exists("Currency Exchange", doc_name):
                        frappe.db.set_value("Currency Exchange", doc_name, "exchange_rate", rate)
                        updated += 1
                    else:
                        doc = frappe.new_doc("Currency Exchange")
                        doc.date          = date_db
                        doc.from_currency = code
                        doc.to_currency   = TO_CURRENCY
                        doc.exchange_rate = rate
                        doc.for_buying    = 1
                        doc.for_selling   = 1
                        doc.insert(ignore_permissions=True)
                        created += 1

            frappe.db.commit()

        except Exception as e:
            frappe.log_error(title=f"CBAR fetch failed: {date_cbar}", message=str(e))
            skipped.append(date_cbar)

        if idx < total - 1:
            time.sleep(random.uniform(0.3, 0.5))

    frappe.publish_realtime(
        "cbar_import_complete",
        {"created": created, "updated": updated, "skipped_dates": skipped},
        user=user,
    )
