from django.http import QueryDict
from workouts.forms import WorkoutSetsField, WorkoutSetsWidget
from workouts.models import WorkoutLogData
from workouts.utils import lbs_to_kg


def build_post_data(exercise_id, rows):
    qd = QueryDict("", mutable=True)
    qd.setlist("workouts_exercise_id", [str(r["exercise_id"]) for r in rows])
    qd.setlist("workouts_sets", [str(r.get("sets") or "") for r in rows])
    qd.setlist("workouts_reps", [str(r.get("reps") or "") for r in rows])
    qd.setlist("workouts_weight", [str(r.get("weight") or "") for r in rows])
    qd.setlist("workouts_notes", [r.get("notes") or "" for r in rows])
    return qd


class TestWorkoutSetsField:
    def field_value(self, field, qd):
        return field.widget.value_from_datadict(qd, {}, "workouts")

    def test_clean_metric(self, exercise):
        field = WorkoutSetsField(units="metric")
        qd = build_post_data(
            exercise.id,
            [{"exercise_id": exercise.id, "sets": 3, "reps": 10, "weight": 50}],
        )
        result = field.clean(self.field_value(field, qd))
        assert result[0]["exercise_id"] == exercise.id
        assert result[0]["sets"] == 3
        assert result[0]["reps"] == 10
        assert result[0]["weight_kg"] == 50.0

    def test_clean_imperial(self, exercise):
        field = WorkoutSetsField(units="imperial")
        qd = build_post_data(
            exercise.id,
            [{"exercise_id": exercise.id, "sets": 3, "reps": 10, "weight": 45}],
        )
        result = field.clean(self.field_value(field, qd))
        assert result[0]["weight_kg"] == lbs_to_kg(45)

    def test_clean_empty(self, exercise):
        field = WorkoutSetsField(units="metric")
        qd = build_post_data(
            exercise.id, [{"exercise_id": "", "sets": "", "reps": "", "weight": ""}]
        )
        assert field.clean(self.field_value(field, qd)) is None

    def test_clean_missing_exercise(self, exercise):
        field = WorkoutSetsField(units="metric")
        qd = build_post_data(
            exercise.id,
            [{"exercise_id": "", "sets": 3, "reps": 10, "weight": 50}],
        )
        assert field.clean(self.field_value(field, qd)) is None


class TestWorkoutLogDataForm:
    def test_form_valid_metric(self, exercise):
        form = WorkoutLogData.form()(
            build_post_data(
                exercise.id,
                [
                    {"exercise_id": exercise.id, "sets": 4, "reps": 8, "weight": 45},
                    {
                        "exercise_id": exercise.id,
                        "sets": 3,
                        "reps": 12,
                        "weight": 30,
                        "notes": "drop set",
                    },
                ],
            )
        )
        WorkoutLogData.prepare_form(form, "metric")
        assert form.is_valid(), form.errors
        data = form.cleaned_data
        assert data["workouts"][0]["sets"] == 4
        assert data["workouts"][0]["weight_kg"] == 45.0
        assert data["workouts"][1]["notes"] == "drop set"

    def test_form_valid_imperial(self, exercise):
        form = WorkoutLogData.form()(
            build_post_data(
                exercise.id,
                [{"exercise_id": exercise.id, "sets": 3, "reps": 10, "weight": 100}],
            )
        )
        WorkoutLogData.prepare_form(form, "imperial")
        assert form.is_valid(), form.errors
        data = form.cleaned_data
        assert data["workouts"][0]["weight_kg"] == lbs_to_kg(100)

    def test_form_bodyweight_imperial(self):
        qd = QueryDict("", mutable=True)
        qd["bodyweight_kg"] = "180.5"
        form = WorkoutLogData.form()(qd)
        WorkoutLogData.prepare_form(form, "imperial")
        assert form.is_valid(), form.errors
        data = form.cleaned_data.copy()
        WorkoutLogData.normalize_form_data(data, "imperial")
        assert data["bodyweight_kg"] == lbs_to_kg(180.5)


class TestWorkoutSetsWidget:
    def test_value_from_datadict(self, exercise):
        widget = WorkoutSetsWidget()
        qd = build_post_data(
            exercise.id,
            [{"exercise_id": exercise.id, "sets": 3, "reps": 10, "weight": 50}],
        )
        value = widget.value_from_datadict(qd, {}, "workouts")
        assert value["exercise_id"] == [str(exercise.id)]
        assert value["weight"] == ["50"]
