"""Dashboard views: home, run list, run detail, run status."""
from django.shortcuts import render, get_object_or_404
from django.http import JsonResponse

from analysis.models import AnalysisRun


def dashboard(request):
    """Home page: recent runs overview."""
    runs = AnalysisRun.objects.all()[:20]
    return render(request, 'analysis/dashboard.html', {'runs': runs})


def run_list(request):
    """List all analysis runs with pagination and optional ?type= filter."""
    run_type_filter = request.GET.get('type')
    page = int(request.GET.get('page', 1))
    per_page = 20

    qs = AnalysisRun.objects.all()
    if run_type_filter in dict(AnalysisRun.RUN_TYPE_CHOICES):
        qs = qs.filter(run_type=run_type_filter)

    total = qs.count()
    runs = qs[(page - 1) * per_page: page * per_page]
    return render(request, 'analysis/run_list.html', {
        'runs': runs,
        'page': page,
        'total_pages': (total + per_page - 1) // per_page if total else 1,
        'current_type': run_type_filter or '',
    })


def run_detail(request, display_id):
    """Details for a single analysis run."""
    import json
    run = get_object_or_404(AnalysisRun, display_id=display_id)
    clusters = run.clusters.all().order_by('-size')

    # ── Global SVD features (compute on-demand from the stored run data) ──
    svd_features = []
    svd_explained_var = []
    try:
        from analysis.data_loader import load_csv_directory, load_from_db
        import polars as pl
        from sklearn.decomposition import TruncatedSVD
        import numpy as np
        import logging
        logger = logging.getLogger(__name__)

        lf = None
        if run.sqlite_table:
            lf = load_from_db(run.sqlite_table)
        if lf is None and run.csv_glob:
            lf, _, _, _, _ = load_csv_directory(run.csv_glob)

        if lf is not None:
            schema = lf.collect_schema()
            numeric_types = (pl.Int8, pl.Int16, pl.Int32, pl.Int64,
                             pl.UInt8, pl.UInt16, pl.UInt32, pl.UInt64,
                             pl.Float32, pl.Float64)
            num_cols = [n for n, dt in zip(schema.names(), schema.dtypes()) if dt in numeric_types and not n.startswith('_')]

            if len(num_cols) >= 3:
                n_rows_lf = lf.select(pl.len()).collect(streaming=True).item()
                sample = min(int(n_rows_lf), 50000)
                df = lf.sample(n=sample, seed=42).select(num_cols).collect(streaming=True) if n_rows_lf > sample else lf.select(num_cols).collect(streaming=True)
                mat = np.nan_to_num(df.to_numpy(), nan=0.0)
                from sklearn.preprocessing import StandardScaler
                mat = StandardScaler().fit_transform(mat)

                n_comp = min(20, min(mat.shape) - 1)
                if n_comp >= 2:
                    svd = TruncatedSVD(n_components=n_comp, random_state=42)
                    svd.fit(mat)
                    var_ratio = svd.explained_variance_ratio_.tolist()
                    svd_explained_var = var_ratio
                    for comp_idx in range(min(10, n_comp)):
                        loadings = np.abs(svd.components_[comp_idx])
                        top_idx = np.argsort(loadings)[-5:][::-1]
                        features = [{'feature': num_cols[i], 'strength': round(float(loadings[i]), 4)} for i in top_idx]
                        svd_features.append({
                            'component': comp_idx + 1,
                            'explained_var': round(var_ratio[comp_idx] * 100, 2),
                            'cumulative_var': round(sum(var_ratio[:comp_idx + 1]) * 100, 2),
                            'top_features': features,
                        })
    except Exception as exc:
        logger = logging.getLogger(__name__)
        logger.warning(f'SVD feature extraction failed for run {run.display_id}: {exc}')

    # ── LLM workflow timeline ──
    llm_timeline_json = ''
    if run.run_type == 'auto':
        llm_thinking = run.llm_thinking or ''
        tool_calls = run.tool_calls_json or []
        if tool_calls or llm_thinking:
            llm_timeline_json = json.dumps({
                'thinking': llm_thinking,
                'tool_calls': tool_calls,
            })

    return render(request, 'analysis/run_detail.html', {
        'run': run,
        'clusters': clusters,
        'svd_features': svd_features,
        'svd_explained_var': svd_explained_var,
        'llm_timeline_json': llm_timeline_json,
    })


def run_status_api(request, display_id):
    """Return current run status as JSON."""
    run = get_object_or_404(AnalysisRun, display_id=display_id)
    return JsonResponse({
        'id': run.display_id,
        'status': run.status,
        'entity_count': run.entity_count,
        'cluster_count': run.cluster_count,
        'error_message': run.error_message,
        'progress_pct': run.progress_pct,
        'progress_msg': run.progress_msg,
        'run_log': run.run_log[-2000:],  # last 2000 chars
        'llm_thinking': run.llm_thinking[-50000:] if run.llm_thinking else '',
        'tool_calls': run.tool_calls_json,
    })


def start_analysis(request):
    """Placeholder: API endpoint to trigger analysis via MCP."""
    return JsonResponse({'status': 'not_implemented', 'message': 'Use MCP tools to trigger analysis'})
