from datetime import date

from django.http import HttpResponse
from django.utils import timezone
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
from rest_framework import status
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from .serializers import (
    IstatCalendarRequestSerializer,
    IstatCalendarResponseSerializer,
    IstatFixIssuesRequestSerializer,
    IstatFixIssuesResponseSerializer,
    IstatExportRequestSerializer,
    IstatIssuesSummaryQuerySerializer,
    IstatIssuesSummaryResponseSerializer,
    IstatMunicipalityQuerySerializer,
    IstatMunicipalityResponseSerializer,
    IstatPreviewRequestSerializer,
)
from .municipalities import search_municipalities, serialize_municipality
from .services import (
    apply_bulk_issue_fixes,
    build_daily_calendar_for_period,
    build_issues_summary,
    generate_export_for_date_range,
    generate_export_for_period,
    generate_preview_for_period,
)


def _parse_iso_date(value, field_name: str):
    if value is None:
        return None
    raw = str(value).strip()
    if not raw:
        return None
    try:
        return date.fromisoformat(raw)
    except ValueError as exc:
        raise ValueError(f"{field_name} must be in YYYY-MM-DD format.") from exc


class IstatExportAPIView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        tags=["ISTAT"],
        summary="Export ISTAT TXT file",
        parameters=[
            OpenApiParameter(
                name="structure_id",
                type=int,
                location=OpenApiParameter.QUERY,
                required=True,
                description="Structure ID",
            ),
            OpenApiParameter(
                name="start_date",
                type=str,
                location=OpenApiParameter.QUERY,
                required=True,
                description="Start date (YYYY-MM-DD)",
            ),
            OpenApiParameter(
                name="end_date",
                type=str,
                location=OpenApiParameter.QUERY,
                required=True,
                description="End date (YYYY-MM-DD)",
            ),
        ],
        responses={
            200: OpenApiResponse(description="ISTAT TXT file download"),
            400: OpenApiResponse(description="Validation error"),
        },
    )
    def get(self, request):
        structure_id_raw = request.query_params.get("structure_id")
        start_date_raw = request.query_params.get("start_date")
        end_date_raw = request.query_params.get("end_date")

        if not structure_id_raw:
            return Response(
                {"detail": "structure_id is required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            structure_id = int(structure_id_raw)
        except (TypeError, ValueError):
            return Response(
                {"detail": "structure_id must be an integer."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            start_date = _parse_iso_date(start_date_raw, "start_date")
            end_date = _parse_iso_date(end_date_raw, "end_date")
        except ValueError as exc:
            return Response(
                {"detail": str(exc)},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if not start_date or not end_date:
            return Response(
                {"detail": "start_date and end_date are required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if start_date > end_date:
            return Response(
                {"detail": "start_date cannot be later than end_date."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            export_payload = generate_export_for_date_range(
                structure_id=structure_id,
                start_date=start_date,
                end_date=end_date,
            )
        except ValueError as exc:
            return Response(
                {"detail": str(exc)},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if export_payload["valid_count"] <= 0:
            return Response(
                {
                    "detail": "No valid ISTAT records available for export.",
                    "invalid_records": export_payload["invalid_records"],
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        response = HttpResponse(
            export_payload["content"],
            content_type="text/plain; charset=ascii",
        )
        response["Content-Disposition"] = (
            f'attachment; filename="{export_payload["filename"]}"'
        )
        response["X-ISTAT-Valid-Records"] = str(export_payload["valid_count"])
        response["X-ISTAT-Invalid-Records"] = str(
            len(export_payload["invalid_records"])
        )
        response["X-ISTAT-Generated-At"] = timezone.now().isoformat()
        return response


class IstatMunicipalityListAPIView(APIView):
    permission_classes = [AllowAny]

    @extend_schema(
        tags=["ISTAT"],
        summary="Search official ISTAT Italian municipalities",
        parameters=[
            OpenApiParameter(
                name="province",
                type=str,
                location=OpenApiParameter.QUERY,
                required=False,
                description="Optional Italian province sigla (for example BO).",
            ),
            OpenApiParameter(
                name="search",
                type=str,
                location=OpenApiParameter.QUERY,
                required=False,
                description="Optional municipality name/code search text.",
            ),
            OpenApiParameter(
                name="limit",
                type=int,
                location=OpenApiParameter.QUERY,
                required=False,
                description="Maximum results to return (1-100).",
            ),
        ],
        responses={200: IstatMunicipalityResponseSerializer},
    )
    def get(self, request):
        serializer = IstatMunicipalityQuerySerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        params = serializer.validated_data
        municipalities = search_municipalities(
            province=params.get("province"),
            search=params.get("search"),
            limit=params.get("limit", 25),
        )
        return Response(
            {
                "results": [
                    serialize_municipality(municipality)
                    for municipality in municipalities
                ]
            }
        )


class IstatPreviewAPIView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        tags=["ISTAT"],
        summary="Preview ISTAT export data",
        request=IstatPreviewRequestSerializer,
        responses={
            200: OpenApiResponse(description="ISTAT preview data"),
            400: OpenApiResponse(description="Validation error"),
        },
    )
    def post(self, request, structure_id):
        serializer = IstatPreviewRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            preview = generate_preview_for_period(
                structure_id=structure_id,
                period=serializer.validated_data["period"],
            )
        except ValueError as exc:
            return Response(
                {"detail": str(exc)},
                status=status.HTTP_400_BAD_REQUEST,
            )

        return Response(
            {
                "summary": preview["summary"],
                "records": preview["record_payloads"],
                "invalid_records": preview["invalid_records"],
            }
        )


class IstatCalendarAPIView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        tags=["ISTAT"],
        summary="Get daily ISTAT calendar metrics for a structure",
        request=IstatCalendarRequestSerializer,
        responses={
            200: IstatCalendarResponseSerializer,
            400: OpenApiResponse(description="Validation error"),
        },
    )
    def post(self, request, structure_id):
        serializer = IstatCalendarRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            payload = build_daily_calendar_for_period(
                structure_id=structure_id,
                period=serializer.validated_data["period"],
            )
        except ValueError as exc:
            return Response(
                {"detail": str(exc)},
                status=status.HTTP_400_BAD_REQUEST,
            )

        return Response(payload)


class IstatIssuesSummaryAPIView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        tags=["ISTAT"],
        summary="Get grouped ISTAT validation issues",
        parameters=[
            OpenApiParameter(
                name="year",
                type=int,
                location=OpenApiParameter.QUERY,
                required=True,
                description="Period year",
            ),
            OpenApiParameter(
                name="from_month",
                type=int,
                location=OpenApiParameter.QUERY,
                required=True,
                description="Start month (1-12)",
            ),
            OpenApiParameter(
                name="to_month",
                type=int,
                location=OpenApiParameter.QUERY,
                required=True,
                description="End month (1-12)",
            ),
        ],
        responses={
            200: IstatIssuesSummaryResponseSerializer,
            400: OpenApiResponse(description="Validation error"),
        },
    )
    def get(self, request, structure_id):
        serializer = IstatIssuesSummaryQuerySerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)

        try:
            payload = build_issues_summary(
                structure_id=structure_id,
                period=serializer.validated_data,
            )
        except ValueError as exc:
            return Response(
                {"detail": str(exc)},
                status=status.HTTP_400_BAD_REQUEST,
            )

        return Response(payload)


class IstatFixIssuesAPIView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        tags=["ISTAT"],
        summary="Apply bulk fixes to ISTAT issues",
        request=IstatFixIssuesRequestSerializer,
        responses={
            200: IstatFixIssuesResponseSerializer,
            400: OpenApiResponse(description="Validation error"),
        },
    )
    def post(self, request, structure_id):
        serializer = IstatFixIssuesRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            result = apply_bulk_issue_fixes(
                structure_id=structure_id,
                period=serializer.validated_data["period"],
                fixes=serializer.validated_data["fixes"],
            )
        except ValueError as exc:
            return Response(
                {"detail": str(exc)},
                status=status.HTTP_400_BAD_REQUEST,
            )

        return Response(result)


class IstatExportStructureAPIView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        tags=["ISTAT"],
        summary="Export ISTAT TXT file (structure)",
        request=IstatExportRequestSerializer,
        responses={
            200: OpenApiResponse(description="ISTAT TXT file download"),
            400: OpenApiResponse(description="Validation error"),
        },
    )
    def post(self, request, structure_id):
        serializer = IstatExportRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            export_payload = generate_export_for_period(
                structure_id=structure_id,
                period=serializer.validated_data["period"],
            )
        except ValueError as exc:
            return Response(
                {"detail": str(exc)},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if export_payload["valid_count"] <= 0:
            return Response(
                {
                    "detail": "No valid ISTAT records available for export.",
                    "invalid_records": export_payload["invalid_records"],
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        response = HttpResponse(
            export_payload["content"],
            content_type="text/plain; charset=ascii",
        )
        response["Content-Disposition"] = (
            f'attachment; filename="{export_payload["filename"]}"'
        )
        response["X-ISTAT-Valid-Records"] = str(export_payload["valid_count"])
        response["X-ISTAT-Invalid-Records"] = str(
            len(export_payload["invalid_records"])
        )
        response["X-ISTAT-Generated-At"] = timezone.now().isoformat()
        return response
