from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
from flask_login import login_required, current_user
from app import db
from app.models import Penduduk, ClusteringSession, ClusterResult
from app.services.clustering_service import run_clustering_pipeline
from app.services.geocoding_service import build_map_data

clustering_bp = Blueprint('clustering', __name__, url_prefix='/clustering')


@clustering_bp.route('/')
@login_required
def index():
    sessions = ClusteringSession.query.order_by(
        ClusteringSession.created_at.desc()
    ).all()
    return render_template('clustering/index.html', sessions=sessions)


@clustering_bp.route('/buat', methods=['GET', 'POST'])
@login_required
def buat():
    tahap_list = db.session.query(
        Penduduk.tahap, db.func.count(Penduduk.id)
    ).group_by(Penduduk.tahap).all()

    if request.method == 'POST':
        tahap          = request.form.get('tahap')
        metode         = request.form.get('metode')
        normalisasi    = request.form.get('normalisasi')
        n_clusters     = request.form.get('n_clusters', type=int)
        linkage_method = request.form.get('linkage_method', 'ward')
        nama_sesi      = request.form.get('nama_sesi', '').strip()
        enable_map     = request.form.get('enable_map', 'on') == 'on'

        if not nama_sesi:
            nama_sesi = f'Analisis {tahap} - {metode.upper()}'

        if not all([tahap, metode, normalisasi, n_clusters]):
            flash('Semua field wajib diisi.', 'danger')
            return render_template('clustering/buat.html', tahap_list=tahap_list)

        if n_clusters < 2 or n_clusters > 10:
            flash('Jumlah cluster harus antara 2 dan 10.', 'danger')
            return render_template('clustering/buat.html', tahap_list=tahap_list)

        penduduk_list = Penduduk.query.filter_by(tahap=tahap).all()

        if len(penduduk_list) < n_clusters:
            flash(f'Data tidak cukup. Ditemukan {len(penduduk_list)} data, minimal {n_clusters}.', 'danger')
            return render_template('clustering/buat.html', tahap_list=tahap_list)

        try:
            # ── Jalankan clustering ──────────────────────────────────────
            result = run_clustering_pipeline(
                penduduk_list=penduduk_list,
                metode=metode,
                normalisasi=normalisasi,
                n_clusters=n_clusters,
                linkage_method=linkage_method,
            )

            # ── Simpan sesi ──────────────────────────────────────────────
            sesi = ClusteringSession(
                nama_sesi      = nama_sesi,
                tahap          = tahap,
                metode         = metode,
                normalisasi    = normalisasi,
                n_cluster      = n_clusters,
                linkage_method = linkage_method if metode == 'hierarchical' else None,
                silhouette_score = result['silhouette_score'],
                jumlah_data    = result['jumlah_data'],
                kesimpulan     = result['kesimpulan'],
                rekomendasi    = result['rekomendasi'],
                dendrogram_img = result['dendrogram_b64'],
                created_by     = current_user.id,
            )
            sesi.centroids = result['centroids']
            sesi.fitur     = result['features']

            db.session.add(sesi)
            db.session.flush()

            # ── Simpan hasil per penduduk ────────────────────────────────
            df_result = result['df_result']
            for _, row in df_result.iterrows():
                cr = ClusterResult(
                    session_id    = sesi.id,
                    penduduk_id   = int(row['id']),
                    cluster_label = int(row['cluster_label']),
                    prioritas     = row['prioritas'],
                )
                db.session.add(cr)

            # ── Geocoding untuk peta (opsional, bisa lambat) ─────────────
            map_markers = []
            if enable_map:
                try:
                    flash('Memproses koordinat peta (Nominatim geocoding)… ini mungkin memakan waktu beberapa detik.', 'info')
                    map_markers = build_map_data(df_result, result['priority_map'])
                    sesi.map_data = map_markers
                except Exception as geo_err:
                    # Geocoding gagal tidak menggagalkan clustering
                    import logging
                    logging.getLogger(__name__).warning(f"Geocoding gagal: {geo_err}")

            db.session.commit()
            flash('Clustering berhasil dijalankan!', 'success')
            return redirect(url_for('clustering.detail', id=sesi.id))

        except ValueError as e:
            db.session.rollback()
            flash(str(e), 'danger')
        except Exception as e:
            db.session.rollback()
            flash(f'Terjadi kesalahan: {str(e)}', 'danger')

    return render_template('clustering/buat.html', tahap_list=tahap_list)


@clustering_bp.route('/detail/<int:id>')
@login_required
def detail(id):
    sesi = ClusteringSession.query.get_or_404(id)

    results = ClusterResult.query.filter_by(session_id=id)\
        .order_by(ClusterResult.cluster_label).all()

    cluster_groups = {}
    for r in results:
        cl = r.cluster_label
        if cl not in cluster_groups:
            cluster_groups[cl] = {
                'label'    : cl,
                'prioritas': r.prioritas,
                'members'  : [],
                'count'    : 0,
            }
        cluster_groups[cl]['members'].append(r)
        cluster_groups[cl]['count'] += 1

    return render_template('clustering/detail.html',
        sesi=sesi,
        cluster_groups=cluster_groups,
        results=results,
        has_map=bool(sesi.map_data),
    )


@clustering_bp.route('/hapus/<int:id>', methods=['POST'])
@login_required
def hapus(id):
    sesi = ClusteringSession.query.get_or_404(id)
    db.session.delete(sesi)
    db.session.commit()
    flash('Sesi clustering berhasil dihapus.', 'success')
    return redirect(url_for('clustering.index'))


@clustering_bp.route('/api/cluster-data/<int:id>')
@login_required
def api_cluster_data(id):
    """API distribusi cluster untuk chart."""
    results = ClusterResult.query.filter_by(session_id=id).all()
    from collections import Counter
    counts     = Counter(r.cluster_label for r in results)
    priorities = {r.cluster_label: r.prioritas for r in results}
    data = [
        {'cluster': k + 1, 'count': v, 'prioritas': priorities.get(k, '-')}
        for k, v in sorted(counts.items())
    ]
    return jsonify(data)


@clustering_bp.route('/api/map-data/<int:id>')
@login_required
def api_map_data(id):
    """
    API data peta untuk Leaflet.
    Return list marker per RW dengan info prioritas.
    """
    sesi = ClusteringSession.query.get_or_404(id)
    markers = sesi.map_data or []

    # Warna per prioritas
    WARNA = {
        'Prioritas Tinggi': '#ef4444',
        'Prioritas Sedang': '#f59e0b',
        'Prioritas Rendah': '#10b981',
    }

    # Tambah info warna ke tiap marker
    for m in markers:
        m['warna'] = WARNA.get(m.get('prioritas_dominan', ''), '#6b7280')

    return jsonify({
        'markers': markers,
        'total_rw': len(markers),
    })


@clustering_bp.route('/peta/<int:id>')
@login_required
def peta(id):
    """Halaman peta fullscreen."""
    sesi = ClusteringSession.query.get_or_404(id)
    return render_template('clustering/peta.html', sesi=sesi)


@clustering_bp.route('/regeocode/<int:id>', methods=['POST'])
@login_required
def regeocode(id):
    """Jalankan ulang geocoding untuk sesi yang belum punya data peta."""
    sesi = ClusteringSession.query.get_or_404(id)

    results   = ClusterResult.query.filter_by(session_id=id).all()
    penduduk  = {r.penduduk_id: r for r in results}

    # Rekonstruksi df_result sederhana dari DB
    import pandas as pd
    rows = []
    for r in results:
        p = r.penduduk
        rows.append({
            'id'           : p.id,
            'rw'           : p.rw,
            'kelurahan'    : p.kelurahan,
            'kecamatan'    : p.kecamatan,
            'kota'         : p.kota,
            'cluster_label': r.cluster_label,
            'prioritas'    : r.prioritas,
        })
    df = pd.DataFrame(rows)

    # Rekonstruksi priority_map dari hasil yang ada
    priority_map = {}
    for r in results:
        if r.cluster_label not in priority_map:
            priority_map[r.cluster_label] = r.prioritas

    try:
        markers = build_map_data(df, priority_map)
        sesi.map_data = markers
        db.session.commit()
        flash(f'Geocoding selesai. {len(markers)} RW berhasil dipetakan.', 'success')
    except Exception as e:
        db.session.rollback()
        flash(f'Geocoding gagal: {str(e)}', 'danger')

    return redirect(url_for('clustering.detail', id=id))