import os
import re
import time
from pathlib import Path
from datetime import datetime
from flask import Flask, render_template, request, jsonify, send_from_directory, make_response
from werkzeug.utils import secure_filename

from config import Config
from tracker_manager import TrackerManager
from automation.browser_manager import BrowserManager
from automation.gemini_brain import GeminiBrain
from automation.poster_engine import PosterEngine

app = Flask(__name__)
app.config["SECRET_KEY"] = Config.SECRET_KEY
app.config["UPLOAD_FOLDER"] = str(Config.UPLOAD_FOLDER)
app.config["MAX_CONTENT_LENGTH"] = Config.MAX_CONTENT_LENGTH

# Ensure uploads directory exists
Config.UPLOAD_FOLDER.mkdir(parents=True, exist_ok=True)

# Initialize core services
tracker_manager = TrackerManager()
browser_manager = BrowserManager()
gemini_brain = GeminiBrain(api_key=Config.GEMINI_API_KEY, model_name=Config.GEMINI_MODEL)
poster_engine = PosterEngine(browser_manager=browser_manager, brain=gemini_brain)

def allowed_file(filename: str) -> bool:
    return "." in filename and filename.rsplit(".", 1)[1].lower() in Config.ALLOWED_EXTENSIONS

@app.after_request
def add_header(response):
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "0"
    return response

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/uploads/<path:filename>")
def serve_upload(filename):
    return send_from_directory(app.config["UPLOAD_FOLDER"], filename)

# Initialize Experimental Studio if enabled
if getattr(Config, "ENABLE_EXPERIMENTAL_STUDIO", False):
    from experimental.flyer_generator import FlyerGenerator
    flyer_generator = FlyerGenerator()

@app.route("/uploads/ai_flyers/<path:filename>")
def serve_ai_flyer(filename):
    return send_from_directory(str(Config.AI_FLYERS_FOLDER), filename)

@app.route("/api/experimental/samples", methods=["GET"])
def get_sample_flyers():
    if not getattr(Config, "ENABLE_EXPERIMENTAL_STUDIO", False):
        return jsonify({"success": False, "error": "Experimental studio disabled."}), 403
    samples = flyer_generator.get_sample_flyers()
    return jsonify({"success": True, "samples": samples})

@app.route("/api/experimental/sample-image/<path:filename>")
def serve_sample_image(filename):
    return send_from_directory(str(Config.CONTOH_FLYER_FOLDER), filename)

@app.route("/api/experimental/generate-flyer", methods=["POST"])
def generate_experimental_flyer():
    if not getattr(Config, "ENABLE_EXPERIMENTAL_STUDIO", False):
        return jsonify({"success": False, "error": "Fitur eksperimental dinonaktifkan."}), 403

    data = request.get_json() or {}
    prompt = data.get("prompt", "").strip()
    theme = data.get("theme", "corporate_blue").strip()
    aspect_ratio = data.get("aspect_ratio", "4:5").strip()
    use_imagen = bool(data.get("use_imagen", True))

    if not prompt:
        return jsonify({"success": False, "error": "Prompt ide flyer wajib diisi."}), 400

    try:
        res = flyer_generator.create_flyer(
            prompt=prompt,
            theme_key=theme,
            aspect_ratio=aspect_ratio,
            use_imagen=use_imagen,
            api_key=gemini_brain.api_key
        )
        return jsonify(res)
    except Exception as e:
        return jsonify({"success": False, "error": str(e)}), 500

@app.route("/api/experimental/use-flyer", methods=["POST"])
def use_experimental_flyer():
    """
    Copies a generated or sample flyer directly into uploads/
    so it seamlessly populates the main campaign form!
    """
    import shutil
    data = request.get_json() or {}
    source_path = data.get("filepath", "").strip()
    suggested_title = data.get("title", "").strip()
    suggested_caption = data.get("caption", "").strip()

    if not source_path or not os.path.exists(source_path):
        return jsonify({"success": False, "error": "File flyer sumber tidak ditemukan."}), 400

    src_p = Path(source_path)
    safe_filename = f"{int(time.time())}_{secure_filename(src_p.name)}"
    dest_path = Config.UPLOAD_FOLDER / safe_filename

    try:
        shutil.copyfile(src_p, dest_path)
        return jsonify({
            "success": True,
            "filename": safe_filename,
            "filepath": str(dest_path),
            "preview_url": f"/uploads/{safe_filename}",
            "suggested_title": suggested_title,
            "suggested_caption": suggested_caption
        })
    except Exception as e:
        return jsonify({"success": False, "error": f"Gagal menyalin flyer ke folder upload: {e}"}), 500

# ======================= SYSTEM & SETTINGS API =======================

@app.route("/api/status", methods=["GET"])
def get_system_status():
    cdp_status = browser_manager.check_cdp_status()
    has_gemini_key = bool(gemini_brain.api_key)

    return jsonify({
        "cdp": cdp_status,
        "gemini": {
            "configured": has_gemini_key,
            "model": gemini_brain.model_name
        },
        "chrome_path": Config.CHROME_EXECUTABLE,
        "total_sosmed_sites": len(tracker_manager.SOSMED_PLATFORMS)
    })

@app.route("/api/research-guides", methods=["GET", "POST"])
def manage_research_guides():
    """
    GET: Retrieve all cached upload & share link guides from site_knowledge.json.
    POST: Trigger Gemini AI research for a specific platform domain and save hint to JSON.
    """
    if request.method == "POST":
        data = request.get_json() or {}
        domain = data.get("domain", "").strip()
        name = data.get("name", domain).strip()
        force = data.get("force_refresh", False)

        if not domain:
            return jsonify({"success": False, "error": "Parameter domain diperlukan."}), 400

        guide = gemini_brain.get_or_research_platform_guide(domain, name, force_refresh=force)
        return jsonify({"success": True, "guide": guide})

    return jsonify({
        "success": True,
        "knowledge": gemini_brain.knowledge
    })

@app.route("/api/settings", methods=["GET", "POST"])
def manage_settings():
    env_file = Config.BASE_DIR / ".env"

    if request.method == "POST":
        data = request.get_json() or {}
        new_key = data.get("gemini_api_key", "").strip()
        new_model = data.get("gemini_model", "gemini-1.5-flash").strip()

        if new_key:
            gemini_brain.set_api_key(new_key)

        if new_model:
            gemini_brain.model_name = new_model

        # Update .env file
        try:
            env_content = ""
            if env_file.exists():
                with open(env_file, "r", encoding="utf-8") as f:
                    env_content = f.read()

            if "GEMINI_API_KEY=" in env_content:
                env_content = re.sub(r"GEMINI_API_KEY=.*", f"GEMINI_API_KEY={new_key}", env_content)
            else:
                env_content += f"\nGEMINI_API_KEY={new_key}"

            if "GEMINI_MODEL=" in env_content:
                env_content = re.sub(r"GEMINI_MODEL=.*", f"GEMINI_MODEL={new_model}", env_content)
            else:
                env_content += f"\nGEMINI_MODEL={new_model}"

            with open(env_file, "w", encoding="utf-8") as f:
                f.write(env_content.strip() + "\n")

            return jsonify({"success": True, "message": "Pengaturan berhasil disimpan ke sistem!"})
        except Exception as e:
            return jsonify({"success": False, "error": f"Gagal menyimpan file .env: {e}"}), 500

    # GET
    masked_key = ""
    if gemini_brain.api_key:
        if len(gemini_brain.api_key) > 8:
            masked_key = gemini_brain.api_key[:4] + "..." + gemini_brain.api_key[-4:]
        else:
            masked_key = "***"

    return jsonify({
        "gemini_api_key_masked": masked_key,
        "has_key": bool(gemini_brain.api_key),
        "gemini_model": gemini_brain.model_name,
        "cdp_port": Config.CDP_PORT
    })

@app.route("/api/settings/test-gemini", methods=["POST"])
def test_gemini():
    data = request.get_json() or {}
    key = data.get("gemini_api_key", "").strip()
    result = gemini_brain.test_connection(api_key=key if key else None)
    return jsonify(result)

@app.route("/api/browser/launch", methods=["POST"])
def launch_browser():
    data = request.get_json(silent=True) or {}
    target_url = data.get("url")
    force_restart = data.get("restart", False)
    if force_restart:
        res = browser_manager.restart_chrome(url=target_url)
    else:
        res = browser_manager.launch_isolated_chrome(url=target_url)
    return jsonify(res)

@app.route("/api/browser/open-tab", methods=["POST"])
def open_browser_tab():
    data = request.get_json() or {}
    target_url = data.get("url", "https://www.google.com").strip()
    status = browser_manager.check_cdp_status()
    if status["active"]:
        success = browser_manager.open_url_in_cdp(target_url)
        if success:
            return jsonify({"success": True, "message": f"Tab baru dibuka di browser bot: {target_url}"})
    # If not active or failed, launch Chrome directly with that URL
    res = browser_manager.launch_isolated_chrome(url=target_url)
    return jsonify(res)

# ======================= VISION LIVE DEBUGGER API =======================

@app.route("/api/vision/history", methods=["GET"])
def get_vision_history():
    """Returns recent vision analysis debug sessions."""
    limit = int(request.args.get("limit", 20))
    history = gemini_brain.get_vision_history(limit=limit)
    return jsonify({"success": True, "history": history, "total": len(history)})

@app.route("/api/vision/inspect-live", methods=["POST"])
def inspect_live_vision():
    """
    Captures the active browser viewport from Chrome Bot via Playwright,
    sends it to Gemini Multimodal Vision, draws visual annotations, and returns
    full inspection payload.
    """
    data = request.get_json(silent=True) or {}
    custom_prompt = data.get("prompt", "").strip()
    if not custom_prompt:
        custom_prompt = "Periksa tampilan halaman ini secara mendalam. Identifikasi modal dialog, tombol posting/kirim/publish, tombol share/bagikan, atau kolom upload yang ada beserta koordinatnya."

    # Verify CDP status
    status = browser_manager.check_cdp_status()
    if not status["active"]:
        return jsonify({
            "success": False,
            "error": "Chrome Bot belum berjalan di port 9222. Silakan buka browser bot terlebih dahulu via tombol 'Buka Browser' atau 'Setup Login Sosmed'."
        }), 400

    try:
        from playwright.sync_api import sync_playwright
        with sync_playwright() as p:
            browser_or_ctx, context, page, mode = browser_manager.acquire_page(p)
            try:
                page.bring_to_front()
            except Exception:
                pass
            time.sleep(0.5)

            # Get page title and URL
            page_title = page.title()
            page_url = page.url

            # Capture viewport screenshot
            ss_bytes = page.screenshot(type="png")

            # Run Gemini Vision analysis with spatial coordinates
            analysis = gemini_brain.analyze_screenshot_with_vision(
                screenshot_bytes=ss_bytes,
                question_or_prompt=custom_prompt,
                source_tag="live_inspector"
            )

            # Disconnect CDP cleanly
            if mode == "cdp" and hasattr(browser_or_ctx, "close"):
                try:
                    browser_or_ctx.close()
                except Exception:
                    pass

            return jsonify({
                "success": True,
                "page_title": page_title,
                "page_url": page_url,
                "analysis": analysis
            })
    except Exception as e:
        return jsonify({"success": False, "error": f"Gagal menganalisis live browser: {e}"}), 500

@app.route("/api/vision/simulate-click", methods=["POST"])
def simulate_vision_click():
    """
    Simulates clicking at the exact (x, y) coordinates on the active page in Chrome Bot.
    """
    data = request.get_json(silent=True) or {}
    coords = data.get("coordinates_percent")
    if not coords or "x" not in coords or "y" not in coords:
        return jsonify({"success": False, "error": "Koordinat persentase (x, y) tidak valid."}), 400

    status = browser_manager.check_cdp_status()
    if not status["active"]:
        return jsonify({"success": False, "error": "Chrome Bot tidak aktif di port 9222."}), 400

    try:
        from playwright.sync_api import sync_playwright
        with sync_playwright() as p:
            browser_or_ctx, context, page, mode = browser_manager.acquire_page(p)
            try:
                page.bring_to_front()
            except Exception:
                pass

            inner_w = page.evaluate("() => window.innerWidth") or 1280
            inner_h = page.evaluate("() => window.innerHeight") or 800

            target_x = int(inner_w * (float(coords["x"]) / 100.0))
            target_y = int(inner_h * (float(coords["y"]) / 100.0))

            page.mouse.move(target_x, target_y, steps=7)
            time.sleep(0.15)
            page.mouse.click(target_x, target_y)
            time.sleep(0.5)

            if mode == "cdp" and hasattr(browser_or_ctx, "close"):
                try:
                    browser_or_ctx.close()
                except Exception:
                    pass

            return jsonify({
                "success": True,
                "clicked_at": {"x": target_x, "y": target_y},
                "message": f"Berhasil mengklik titik ({target_x}px, {target_y}px) pada browser bot!"
            })
    except Exception as e:
        return jsonify({"success": False, "error": f"Gagal simulasi klik: {e}"}), 500

@app.route("/api/vision/clear", methods=["POST"])
def clear_vision():
    success = gemini_brain.clear_vision_history()
    return jsonify({"success": success, "message": "Histori dan screenshot debug berhasil dibersihkan."})

# ======================= TRACKER & CAMPAIGN API =======================

@app.route("/api/sites", methods=["GET"])
def get_sites():
    sites = tracker_manager.get_sosmed_sites()
    campaigns = tracker_manager.get_campaign_columns()
    return jsonify({
        "sites": sites,
        "campaigns": campaigns,
        "total": len(sites)
    })

@app.route("/api/campaigns", methods=["GET", "POST"])
def manage_campaigns():
    if request.method == "POST":
        data = request.get_json() or {}
        name = data.get("name", "").strip()
        if not name:
            return jsonify({"success": False, "error": "Nama kampanye tidak boleh kosong."}), 400
        col_idx = tracker_manager.add_new_campaign_column(name)
        return jsonify({"success": True, "column_index": col_idx, "campaign_name": name})

    campaigns = tracker_manager.get_campaign_columns()
    return jsonify({"campaigns": campaigns})

@app.route("/api/sites/add", methods=["POST"])
def add_new_site():
    """
    Register a new website to tracker-posting.xlsx and trigger AI guide research.
    """
    data = request.get_json() or {}
    raw_domain = data.get("domain", "").strip()
    name = data.get("name", "").strip()
    tier = data.get("tier", "Tier 2").strip()
    post_type = data.get("post_type", "Social / Media Post").strip()
    auto_research = data.get("auto_research", True)

    if not raw_domain:
        return jsonify({"success": False, "error": "Domain website tidak boleh kosong."}), 400

    created = tracker_manager.add_website(
        domain=raw_domain,
        name=name if name else None,
        tier=tier,
        post_type=post_type
    )

    guide = None
    if auto_research:
        try:
            guide = gemini_brain.get_or_research_platform_guide(created["domain"], created["name"])
        except Exception as e:
            print(f"[AUTO RESEARCH ERROR] {e}")

    return jsonify({
        "success": True,
        "site": created,
        "guide": guide,
        "message": f"Website '{created['name']}' ({created['domain']}) berhasil ditambahkan ke Excel tracker!"
    })

@app.route("/api/tracker/export", methods=["GET"])
def export_tracker():
    """
    Download the tracker spreadsheet as CSV or Excel (.xlsx).
    """
    fmt = request.args.get("format", "excel").lower()
    timestamp = datetime.now().strftime("%Y%m%d_%H%M")

    if fmt == "csv":
        csv_data = tracker_manager.export_to_csv()
        response = make_response(csv_data)
        response.headers["Content-Disposition"] = f"attachment; filename=tracker_posting_{timestamp}.csv"
        response.headers["Content-Type"] = "text/csv; charset=utf-8"
        return response
    else:
        excel_bytes = tracker_manager.export_to_excel_bytes()
        response = make_response(excel_bytes)
        response.headers["Content-Disposition"] = f"attachment; filename=tracker_posting_{timestamp}.xlsx"
        response.headers["Content-Type"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        return response

@app.route("/api/research-guides/batch", methods=["POST"])
def batch_research_guides():
    """
    Scan & research upload/share hints for all selected domains before launching campaign.
    """
    data = request.get_json() or {}
    domains = data.get("domains", [])
    force_refresh = data.get("force_refresh", False)

    if not domains:
        return jsonify({"success": False, "error": "Pilih minimal satu platform untuk dipindai."}), 400

    all_sites = {s["domain"]: s["name"] for s in tracker_manager.get_sosmed_sites()}
    results = {}

    for domain in domains:
        d = domain.lower().replace("www.", "")
        platform_name = all_sites.get(d, d.capitalize())
        try:
            guide = gemini_brain.get_or_research_platform_guide(d, platform_name, force_refresh=force_refresh)
            results[d] = {
                "success": True,
                "platform_name": platform_name,
                "domain": d,
                "direct_upload_url": guide.get("direct_upload_url"),
                "has_selectors": bool(guide.get("submit_button_selectors") or guide.get("file_input_selectors")),
                "share_link_strategy": guide.get("share_link_guide", {}).get("strategy", "current_url"),
                "source": guide.get("source", "gemini_ai")
            }
        except Exception as e:
            results[d] = {
                "success": False,
                "platform_name": platform_name,
                "domain": d,
                "error": str(e)
            }

    return jsonify({
        "success": True,
        "results": results,
        "total": len(results)
    })

# ======================= CONTENT & POSTING API =======================

@app.route("/api/upload-flyer", methods=["POST"])
def upload_flyer():
    if "flyer" not in request.files:
        return jsonify({"success": False, "error": "Tidak ada file flyer yang diunggah."}), 400

    file = request.files["flyer"]
    if file.filename == "":
        return jsonify({"success": False, "error": "Nama file tidak valid."}), 400

    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        # Prefix timestamp to avoid name collisions
        import time
        safe_filename = f"{int(time.time())}_{filename}"
        save_path = Config.UPLOAD_FOLDER / safe_filename
        file.save(str(save_path))

        return jsonify({
            "success": True,
            "filename": safe_filename,
            "filepath": str(save_path),
            "preview_url": f"/uploads/{safe_filename}"
        })

    return jsonify({"success": False, "error": "Format file tidak didukung (gunakan JPG, PNG, WEBP, GIF, MP4)."}), 400

@app.route("/api/generate-caption", methods=["POST"])
def generate_caption():
    data = request.get_json() or {}
    title = data.get("title", "").strip()
    description = data.get("description", "").strip()
    platform = data.get("platform", "Instagram").strip()
    link = data.get("link", "").strip()

    if not title:
        return jsonify({"success": False, "error": "Judul kampanye wajib diisi."}), 400

    caption = gemini_brain.generate_social_caption(
        title=title,
        description=description,
        platform_name=platform,
        target_link=link if link else None
    )

    return jsonify({
        "success": True,
        "platform": platform,
        "caption": caption
    })

@app.route("/api/campaign/start", methods=["POST"])
def start_campaign():
    data = request.get_json() or {}
    campaign_name = data.get("campaign_name", "").strip()
    flyer_path = data.get("flyer_path", "").strip()
    title = data.get("title", "").strip()
    caption = data.get("caption", "").strip()
    target_link = data.get("target_link", "").strip()
    selected_domains = data.get("selected_domains", [])

    if not campaign_name:
        return jsonify({"success": False, "error": "Kolom kampanye target wajib dipilih."}), 400
    if not flyer_path or not os.path.exists(flyer_path):
        return jsonify({"success": False, "error": "File flyer belum diunggah atau tidak ditemukan."}), 400
    if not title:
        return jsonify({"success": False, "error": "Judul promosi wajib diisi."}), 400
    if not selected_domains:
        return jsonify({"success": False, "error": "Pilih minimal satu platform media sosial sasaran."}), 400

    job_id = poster_engine.start_campaign(
        campaign_name=campaign_name,
        flyer_path=flyer_path,
        title=title,
        caption=caption,
        target_link=target_link if target_link else None,
        selected_domains=selected_domains
    )

    return jsonify({
        "success": True,
        "job_id": job_id,
        "message": f"Kampanye '{campaign_name}' berhasil diluncurkan!"
    })

@app.route("/api/campaign/status/<job_id>", methods=["GET"])
def get_campaign_status(job_id):
    job = poster_engine.get_job(job_id)
    if not job:
        return jsonify({"success": False, "error": "Job tidak ditemukan."}), 404
    return jsonify({"success": True, "job": job})

@app.route("/api/campaign/stop/<job_id>", methods=["POST"])
def stop_campaign(job_id):
    success = poster_engine.stop_campaign(job_id)
    return jsonify({"success": success})

if __name__ == "__main__":
    print(f"=== DSE AUTOMATION SOSMED SERVER STARTING ===")
    print(f"URL Dashboard: http://{Config.HOST}:{Config.PORT}")
    print(f"Chrome Dev CDP Target: {Config.CDP_URL}")
    app.run(host=Config.HOST, port=Config.PORT, debug=Config.DEBUG)
