#!/usr/bin/env python3
"""
Deploy Financial Accounting Pages to Production
Haven Grazuri Investment Limited

Uploads all changed files to production via FTP, then triggers app restart.

Usage:
    python deploy_financial_pages.py
    python deploy_financial_pages.py --dry-run      (list files only, no upload)
    python deploy_financial_pages.py --migrate-only  (only run migration reminder)

Server: grazuri.uzuriapps.xyz
Remote root: /home/xygbfpsg/phingrazuri/
"""

import os
import sys
import ftplib
import time
from pathlib import Path

# ── Configuration ──────────────────────────────────────────────────────────
FTP_HOST     = "grazuri.uzuriapps.xyz"
FTP_PORT     = 21
FTP_USER     = "xygbfpsg"          # cPanel username
# Set FTP_PASS as environment variable or pass via CLI to avoid storing in file
FTP_PASS     = os.environ.get("FTP_PASS", "")

LOCAL_ROOT   = Path(__file__).resolve().parent
REMOTE_ROOT  = "/home/xygbfpsg/phingrazuri"

# ── Files to upload: (local_relative, remote_relative) ────────────────────
FILES = [
    # Python source files
    ("accounting/pdf_reports.py",
     "accounting/pdf_reports.py"),

    ("accounting/migrations/0005_add_comprehensive_accounts.py",
     "accounting/migrations/0005_add_comprehensive_accounts.py"),

    ("reports/financial_reports_views.py",
     "reports/financial_reports_views.py"),

    ("reports/urls.py",
     "reports/urls.py"),

    # Templates
    ("templates/reports/financial/index.html",
     "templates/reports/financial/index.html"),

    ("templates/reports/financial/chart_of_accounts.html",
     "templates/reports/financial/chart_of_accounts.html"),

    ("templates/reports/financial/trial_balance.html",
     "templates/reports/financial/trial_balance.html"),

    ("templates/reports/financial/income_statement.html",
     "templates/reports/financial/income_statement.html"),

    ("templates/reports/financial/balance_sheet.html",
     "templates/reports/financial/balance_sheet.html"),

    ("templates/reports/financial/cash_flow_statement.html",
     "templates/reports/financial/cash_flow_statement.html"),

    ("templates/reports/financial/account_ledger.html",
     "templates/reports/financial/account_ledger.html"),
]

# ── Helpers ────────────────────────────────────────────────────────────────

def ok(msg):   print(f"  \033[92m✓\033[0m  {msg}")
def err(msg):  print(f"  \033[91m✗\033[0m  {msg}")
def info(msg): print(f"  \033[94m→\033[0m  {msg}")
def hdr(msg):  print(f"\n\033[1m{'─'*60}\n  {msg}\n{'─'*60}\033[0m")


def ensure_remote_dirs(ftp: ftplib.FTP, remote_path: str):
    """Create remote directory tree if it doesn't exist."""
    parts = remote_path.strip("/").split("/")
    current = ""
    for part in parts:
        current = f"{current}/{part}"
        try:
            ftp.mkd(current)
        except ftplib.error_perm:
            pass  # already exists


def upload_file(ftp: ftplib.FTP, local_path: Path, remote_path: str):
    """Upload a single file, creating parent dirs as needed."""
    remote_dir = "/".join(remote_path.split("/")[:-1])
    if remote_dir:
        ensure_remote_dirs(ftp, remote_dir)
    with open(local_path, "rb") as f:
        ftp.storbinary(f"STOR {remote_path}", f)


def ftp_restart(ftp: ftplib.FTP):
    """Touch restart.txt to trigger Passenger reload."""
    restart_paths = [
        f"{REMOTE_ROOT}/tmp/restart.txt",
        f"{REMOTE_ROOT}/passenger_wsgi.py",
    ]
    for rp in restart_paths:
        try:
            # Touch by overwriting with empty content
            from io import BytesIO
            ftp.storbinary(f"APPE {rp}", BytesIO(b" "))
            ok(f"Touched: {rp}")
            return True
        except Exception:
            pass
    return False


# ── Main ───────────────────────────────────────────────────────────────────

def main():
    dry_run      = "--dry-run"      in sys.argv
    migrate_only = "--migrate-only" in sys.argv

    hdr("Haven Grazuri — Financial Pages Deployment")
    print(f"  Server : {FTP_HOST}")
    print(f"  Remote : {REMOTE_ROOT}")
    print(f"  Files  : {len(FILES)}")
    if dry_run:
        print("  Mode   : DRY RUN (no upload)")

    # ── Verify local files exist ───────────────────────────────────────────
    hdr("Step 1: Verify local files")
    missing = []
    for local_rel, _ in FILES:
        local_abs = LOCAL_ROOT / local_rel
        if local_abs.exists():
            ok(f"{local_rel}  ({local_abs.stat().st_size:,} bytes)")
        else:
            err(f"MISSING: {local_rel}")
            missing.append(local_rel)

    if missing:
        err(f"\n{len(missing)} file(s) missing. Aborting.")
        sys.exit(1)

    if migrate_only:
        hdr("Migration reminder")
        print_migration_steps()
        return

    if dry_run:
        hdr("Dry run complete — no files uploaded")
        print_migration_steps()
        return

    # ── FTP upload ─────────────────────────────────────────────────────────
    if not FTP_PASS:
        print()
        FTP_PASS_input = input(
            "  Enter FTP password for xygbfpsg "
            "(or set env var FTP_PASS): "
        ).strip()
        if not FTP_PASS_input:
            err("No password provided. Aborting.")
            sys.exit(1)
        password = FTP_PASS_input
    else:
        password = FTP_PASS

    hdr("Step 2: Upload files via FTP")
    try:
        info(f"Connecting to {FTP_HOST}:{FTP_PORT} ...")
        ftp = ftplib.FTP()
        ftp.connect(FTP_HOST, FTP_PORT, timeout=30)
        ftp.login(FTP_USER, password)
        ftp.set_pasv(True)
        ok(f"Connected as {FTP_USER}")
    except Exception as e:
        err(f"FTP connection failed: {e}")
        err("Check host, port, username and password.")
        sys.exit(1)

    uploaded = 0
    failed   = []

    for local_rel, remote_rel in FILES:
        local_abs   = LOCAL_ROOT / local_rel
        remote_full = f"{REMOTE_ROOT}/{remote_rel}"
        try:
            upload_file(ftp, local_abs, remote_full)
            ok(f"Uploaded → {remote_full}")
            uploaded += 1
        except Exception as e:
            err(f"FAILED {local_rel}: {e}")
            failed.append(local_rel)

    # ── Restart ────────────────────────────────────────────────────────────
    hdr("Step 3: Trigger app restart")
    restarted = ftp_restart(ftp)
    if not restarted:
        info("Could not touch restart.txt — restart manually in cPanel.")

    ftp.quit()

    # ── Summary ────────────────────────────────────────────────────────────
    hdr("Upload Summary")
    ok(f"Uploaded:  {uploaded}/{len(FILES)} files")
    if failed:
        err(f"Failed:    {len(failed)} files")
        for f in failed:
            err(f"  • {f}")
    else:
        ok("All files uploaded successfully!")

    print_migration_steps()


def print_migration_steps():
    hdr("Step 4: Run migration on server (REQUIRED)")
    print("""
  Log in to cPanel Terminal (or SSH) and run:

    cd /home/xygbfpsg/phingrazuri
    python manage.py migrate accounting

  This adds 88 new accounts (PAYE, NHIF, NSSF, M-Pesa fees, etc.)
  If already run locally, skip — it's idempotent.

  Then restart the app again if not done automatically.
""")
    hdr("Step 5: Verify pages")
    urls = [
        "/reports/financial/",
        "/reports/financial/chart-of-accounts/",
        "/reports/financial/trial-balance/",
        "/reports/financial/income-statement/",
        "/reports/financial/balance-sheet/",
        "/reports/financial/cash-flow/",
        "/reports/financial/chart-of-accounts/pdf/",
        "/reports/financial/trial-balance/pdf/",
        "/reports/financial/income-statement/pdf/",
        "/reports/financial/balance-sheet/pdf/",
    ]
    print("  Visit each URL and confirm 200 / PDF opens:")
    for u in urls:
        print(f"    https://grazuri.uzuriapps.xyz{u}")


if __name__ == "__main__":
    main()
