import importlib
from datetime import datetime
import pandas as pd
from app.tasks import celery_app
from app.extensions import db
from app.models.analysis import AnalysisRun
from app.models.dataset import DataPoint


def fetch_subset(run):
    """Fetch data points matching the run's subset definition as a DataFrame."""
    q = DataPoint.query.filter_by(dataset_id=run.dataset_id)
    if run.animal_ids:
        q = q.filter(DataPoint.animal_id.in_(run.animal_ids))
    if run.time_start:
        q = q.filter(DataPoint.timestamp >= run.time_start)
    if run.time_end:
        q = q.filter(DataPoint.timestamp <= run.time_end)

    points = q.order_by(DataPoint.animal_id, DataPoint.timestamp).all()

    records = []
    for p in points:
        records.append({
            'animal_id': p.animal_id,
            'timestamp': p.timestamp,
            'lat': p.lat,
            'lon': p.lon,
            'speed': p.speed,
            'step_length': p.step_length,
            'turning_angle': p.turning_angle,
        })
    return pd.DataFrame(records)


@celery_app.task(bind=True)
def run_analysis(self, run_id):
    """Execute an analysis run."""
    run = AnalysisRun.query.get(run_id)
    if not run:
        return

    run.status = 'running'
    run.started_at = datetime.utcnow()
    run.celery_task_id = self.request.id
    db.session.commit()

    try:
        df = fetch_subset(run)
        if df.empty:
            raise ValueError('No data points in subset')

        # Dynamically load and call the analysis function
        module = importlib.import_module(run.analysis_type.python_module)
        func = getattr(module, run.analysis_type.python_function)
        result = func(df, run.params or {}, run_id=run.id)

        run.result_summary = result.get('summary', {})
        run.result_artifacts = result.get('artifacts', {})
        run.status = 'completed'
        run.completed_at = datetime.utcnow()

    except Exception as e:
        run.status = 'failed'
        run.error_message = str(e)
        run.completed_at = datetime.utcnow()

    db.session.commit()
