"""
Fix annual subscription billing dates.

For each annual subscription (billing_frequency='A'):
  1. Create an annual copy of its subscription plan (billing_interval=Year)
  2. Re-link the subscription to the annual plan
  3. Set current_invoice_start/end from legacy date_next_invoice
  4. ERPNext scheduler uses plan's billing_interval to determine period length

Run inside erpnext-backend-1:
  /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/fix_annual_billing_dates.py
"""
import frappe
import pymysql
import os
from datetime import datetime, timezone, timedelta

DRY_RUN = False

os.chdir("/home/frappe/frappe-bench/sites")
frappe.init(site="erp.gigafibre.ca", sites_path=".")
frappe.connect()
print("Connected:", frappe.local.site)

conn = pymysql.connect(
    host="legacy-db", user="facturation", password="***LEGACY-DB-PW-REDACTED***",
    database="gestionclient", cursorclass=pymysql.cursors.DictCursor
)

# ═══════════════════════════════════════════════════════════════
# LOAD LEGACY DATA
# ═══════════════════════════════════════════════════════════════
print("Loading legacy annual services...")
with conn.cursor() as cur:
    cur.execute("""
        SELECT s.id, s.date_next_invoice, s.date_orig, s.payment_recurrence,
               s.hijack_price, s.hijack, p.price as base_price, p.sku
        FROM service s
        JOIN product p ON p.id = s.product_id
        WHERE s.status = 1 AND s.payment_recurrence = 0
    """)
    annual_services = cur.fetchall()
conn.close()
print("Annual services in legacy: {}".format(len(annual_services)))

legacy_annual = {s["id"]: s for s in annual_services}

def ts_to_date(ts):
    if not ts or ts <= 0:
        return None
    try:
        return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d")
    except (ValueError, OSError):
        return None

# ═══════════════════════════════════════════════════════════════
# LOAD ANNUAL SUBSCRIPTIONS FROM ERPNEXT
# ═══════════════════════════════════════════════════════════════
print("\nLoading annual subscriptions from ERPNext...")
annual_subs = frappe.db.sql("""
    SELECT s.name, s.legacy_service_id, s.party, s.status,
           s.current_invoice_start, s.current_invoice_end,
           s.billing_frequency, s.start_date,
           spd.plan, spd.name as spd_name
    FROM "tabSubscription" s
    LEFT JOIN "tabSubscription Plan Detail" spd ON spd.parent = s.name
    WHERE s.billing_frequency = 'A'
    ORDER BY s.name
""", as_dict=True)
print("Annual subscriptions: {}".format(len(annual_subs)))

# ═══════════════════════════════════════════════════════════════
# LOAD EXISTING PLANS — need to create annual copies
# ═══════════════════════════════════════════════════════════════
plan_names = set(s["plan"] for s in annual_subs if s["plan"])
print("Distinct plans used by annual subs: {}".format(len(plan_names)))

existing_plans = {}
if plan_names:
    placeholders = ",".join(["%s"] * len(plan_names))
    plans = frappe.db.sql("""
        SELECT name, plan_name, billing_interval, billing_interval_count,
               cost, currency, item, price_determination, price_list
        FROM "tabSubscription Plan"
        WHERE plan_name IN ({})
    """.format(placeholders), list(plan_names), as_dict=True)
    existing_plans = {p["plan_name"]: p for p in plans}
    for p in plans:
        print("  {} — {}, cost={}, interval={} x{}".format(
            p["plan_name"], p["item"], p["cost"], p["billing_interval"], p["billing_interval_count"]))

# Check which annual plan copies already exist
all_annual_plans = frappe.db.sql("""
    SELECT name, plan_name FROM "tabSubscription Plan"
    WHERE billing_interval = 'Year'
""", as_dict=True)
existing_annual_names = {p["plan_name"] for p in all_annual_plans}
print("\nExisting annual plans: {}".format(len(all_annual_plans)))

# ═══════════════════════════════════════════════════════════════
# BUILD UPDATES
# ═══════════════════════════════════════════════════════════════
print("\n" + "=" * 70)
print("BUILDING UPDATES")
print("=" * 70)

updates = []
issues = []
plans_to_create = {}  # monthly_plan_name → annual_plan_name

for sub in annual_subs:
    sid = sub["legacy_service_id"]
    legacy = legacy_annual.get(sid)

    if not legacy:
        issues.append((sub["name"], sid, "No legacy service found"))
        continue

    # Determine billing period from legacy
    next_inv_date = ts_to_date(legacy["date_next_invoice"])
    start_date = ts_to_date(legacy["date_orig"])

    if next_inv_date:
        inv_start_dt = datetime.strptime(next_inv_date, "%Y-%m-%d")
        today_dt = datetime.now()
        # Roll forward past dates to next cycle
        while inv_start_dt < today_dt - timedelta(days=365):
            inv_start_dt = inv_start_dt.replace(year=inv_start_dt.year + 1)
        inv_start = inv_start_dt.strftime("%Y-%m-%d")
        inv_end = (inv_start_dt.replace(year=inv_start_dt.year + 1) - timedelta(days=1)).strftime("%Y-%m-%d")
    elif start_date:
        start_dt = datetime.strptime(start_date, "%Y-%m-%d")
        today_dt = datetime.now()
        candidate = start_dt.replace(year=today_dt.year)
        if candidate < today_dt:
            candidate = candidate.replace(year=today_dt.year + 1)
        inv_start = candidate.strftime("%Y-%m-%d")
        inv_end = (candidate.replace(year=candidate.year + 1) - timedelta(days=1)).strftime("%Y-%m-%d")
    else:
        issues.append((sub["name"], sid, "No dates in legacy"))
        continue

    # Figure out the annual plan name: PLAN-AN-{SKU}
    monthly_plan = sub["plan"]
    if monthly_plan:
        # Extract SKU from plan name (PLAN-FTTH80I → FTTH80I)
        sku_part = monthly_plan.replace("PLAN-", "")
        annual_plan = "PLAN-AN-" + sku_part
        if monthly_plan not in plans_to_create and annual_plan not in existing_annual_names:
            plans_to_create[monthly_plan] = annual_plan
    else:
        annual_plan = None

    updates.append({
        "sub_name": sub["name"],
        "spd_name": sub["spd_name"],
        "legacy_id": sid,
        "party": sub["party"],
        "monthly_plan": monthly_plan,
        "annual_plan": annual_plan,
        "inv_start": inv_start,
        "inv_end": inv_end,
        "sku": legacy.get("sku", "?"),
        "price": float(legacy["hijack_price"]) if legacy["hijack"] else float(legacy["base_price"] or 0),
    })

print("\nUpdates to apply: {}".format(len(updates)))
print("Annual plans to create: {}".format(len(plans_to_create)))
print("Issues: {}".format(len(issues)))

# Show plans to create
for monthly, annual in plans_to_create.items():
    orig = existing_plans.get(monthly, {})
    print("  {} → {} (item: {}, cost: {})".format(
        monthly, annual, orig.get("item", "?"), orig.get("cost", "?")))

# Show sample updates
print("\nSample updates:")
for u in updates[:15]:
    print("  {} svc#{} {} — {} → period {}/{} plan:{} (${:.2f})".format(
        u["sub_name"], u["legacy_id"], u["sku"], u["party"],
        u["inv_start"], u["inv_end"], u["annual_plan"] or "NONE", u["price"]))

if issues:
    print("\nIssues:")
    for name, sid, reason in issues[:10]:
        print("  {} svc#{}: {}".format(name, sid, reason))

# ═══════════════════════════════════════════════════════════════
# APPLY
# ═══════════════════════════════════════════════════════════════
if DRY_RUN:
    print("\n*** DRY RUN — no changes made ***")
    print("Set DRY_RUN = False to apply {} updates + {} new plans".format(
        len(updates), len(plans_to_create)))
else:
    print("\n" + "=" * 70)
    print("APPLYING CHANGES")
    print("=" * 70)
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # Step 0: Fix existing plan names — migration stored SP-xxx but autoname expects plan_name
    # The SPD plan field already stores plan_name, so name must match for Frappe ORM to work
    mismatched = frappe.db.sql("""
        SELECT name, plan_name FROM "tabSubscription Plan"
        WHERE name != plan_name
    """, as_dict=True)
    if mismatched:
        print("Fixing {} plan names (SP-xxx → plan_name)...".format(len(mismatched)))
        for p in mismatched:
            frappe.db.sql("""
                UPDATE "tabSubscription Plan" SET name = %s WHERE name = %s
            """, (p["plan_name"], p["name"]))
        frappe.db.commit()
        print("  Done — plan names now match plan_name field")

    # Step 1: Create annual plan copies
    for monthly_name, annual_name in plans_to_create.items():
        orig = existing_plans.get(monthly_name)
        if not orig:
            print("  SKIP {} — original plan not found".format(monthly_name))
            continue

        try:
            plan = frappe.get_doc({
                "doctype": "Subscription Plan",
                "plan_name": annual_name,
                "item": orig["item"],
                "price_determination": orig.get("price_determination") or "Fixed Rate",
                "cost": orig["cost"],
                "currency": orig.get("currency") or "CAD",
                "billing_interval": "Year",
                "billing_interval_count": 1,
            })
            plan.insert(ignore_permissions=True)
            print("  Created annual plan: {} (item: {}, cost: {})".format(
                annual_name, orig["item"], orig["cost"]))
        except Exception as e:
            print("  ERR creating plan {}: {}".format(annual_name, str(e)[:100]))

    frappe.db.commit()

    # Step 2: Update subscriptions — billing dates + re-link to annual plan
    updated = 0
    plan_switched = 0
    for u in updates:
        # Set billing dates
        frappe.db.sql("""
            UPDATE "tabSubscription"
            SET current_invoice_start = %s,
                current_invoice_end = %s
            WHERE name = %s
        """, (u["inv_start"], u["inv_end"], u["sub_name"]))

        # Switch plan in Subscription Plan Detail
        if u["annual_plan"] and u["spd_name"]:
            frappe.db.sql("""
                UPDATE "tabSubscription Plan Detail"
                SET plan = %s
                WHERE name = %s
            """, (u["annual_plan"], u["spd_name"]))
            plan_switched += 1

        updated += 1

    frappe.db.commit()
    print("\nUpdated {} subscriptions with billing dates".format(updated))
    print("Switched {} subscriptions to annual plans".format(plan_switched))

print("\n" + "=" * 70)
print("DONE")
print("=" * 70)
