from datetime import date, timedelta

import requests
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
from django.shortcuts import redirect
from drf_spectacular.utils import extend_schema

from apps.analytics.utils import client_ip
from apps.core.tenancy import get_active_organization

from .models import URL, URLClick, QRCode, ImageToolUsage, FileConversion, CurrencyRate, ScribdScrape
from .serializers import (
    URLSerializer, URLCreateSerializer, URLClickSerializer,
    QRCodeSerializer, QRCodeCreateSerializer,
    ImageToolUsageSerializer, ImageToolUsageCreateSerializer,
    FileConversionSerializer, FileConversionCreateSerializer,
    ScribdScrapeSerializer, ScribdScrapeCreateSerializer,
)


class URLViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing shortened URLs.
    """
    queryset = URL.objects.select_related("created_by")

    def get_serializer_class(self):
        if self.action in ['create', 'update', 'partial_update']:
            return URLCreateSerializer
        return URLSerializer

    def get_queryset(self):
        user = self.request.user
        if user.is_authenticated:
            return URL.objects.filter(created_by=user, organization=get_active_organization(self.request))
        return URL.objects.none()

    def get_permissions(self):
        # Short links must be followable by anonymous visitors (links shared on
        # social, email, etc). Creation/management stays authenticated.
        if self.action == "redirect_to_original":
            return [AllowAny()]
        return super().get_permissions()

    def perform_create(self, serializer):
        serializer.save(created_by=self.request.user, organization=get_active_organization(self.request))

    @extend_schema(
        description="Redirect to the original URL using short code"
    )
    @action(detail=False, methods=['get'], url_path=r'(?P<short_code>[^/]+)')
    def redirect_to_original(self, request, short_code=None):
        try:
            url = URL.objects.get(short_code=short_code)
            if url.is_expired():
                return Response(
                    {'error': 'This URL has expired'},
                    status=status.HTTP_410_GONE
                )
            url.increment_clicks()

            URLClick.objects.create(
                url=url,
                ip_address=client_ip(request) or None,
                user_agent=request.META.get('HTTP_USER_AGENT', '')[:512],
                referer=request.META.get('HTTP_REFERER', '')
            )

            return redirect(url.original_url)
        except URL.DoesNotExist:
            return Response(
                {'error': 'URL not found'},
                status=status.HTTP_404_NOT_FOUND
            )


class QRCodeViewSet(viewsets.ModelViewSet):
    """
    ViewSet for generating and managing QR codes.
    """
    queryset = QRCode.objects.all()

    def get_serializer_class(self):
        if self.action in ['create', 'update', 'partial_update']:
            return QRCodeCreateSerializer
        return QRCodeSerializer

    def get_queryset(self):
        user = self.request.user
        if user.is_authenticated:
            return QRCode.objects.filter(created_by=user, organization=get_active_organization(self.request)).select_related("created_by")
        return QRCode.objects.none()

    def perform_create(self, serializer):
        serializer.save(created_by=self.request.user, organization=get_active_organization(self.request))

    @extend_schema(
        description="Download QR code as image"
    )
    @action(detail=False, methods=['get'], url_path=r'(?P<short_code>[^/]+)/download')
    def download(self, request, short_code=None):
        import io
        from django.http import HttpResponse

        try:
            qr = QRCode.objects.get(short_code=short_code, created_by=request.user)
        except QRCode.DoesNotExist:
            return Response(
                {'error': 'QR code not found'},
                status=status.HTTP_404_NOT_FOUND
            )

        import qrcode
        qr_image = qrcode.make(qr.content)
        buffer = io.BytesIO()
        qr_image.save(buffer, format=qr.format.upper())
        buffer.seek(0)

        qr.downloads += 1
        qr.save(update_fields=['downloads'])

        return HttpResponse(buffer.getvalue(), content_type=f'image/{qr.format}')


class ImageToolUsageViewSet(viewsets.ModelViewSet):
    """
    ViewSet for tracking image tool usage.
    """
    queryset = ImageToolUsage.objects.all()
    serializer_class = ImageToolUsageSerializer

    def get_serializer_class(self):
        if self.action in ['create', 'update', 'partial_update']:
            return ImageToolUsageCreateSerializer
        return ImageToolUsageSerializer

    def get_queryset(self):
        user = self.request.user
        if user.is_authenticated:
            return ImageToolUsage.objects.filter(created_by=user, organization=get_active_organization(self.request)).select_related("created_by")
        return ImageToolUsage.objects.none()

    def perform_create(self, serializer):
        serializer.save(created_by=self.request.user, organization=get_active_organization(self.request))


class FileConversionViewSet(viewsets.ModelViewSet):
    """
    ViewSet for tracking file conversions.
    """
    queryset = FileConversion.objects.all()
    serializer_class = FileConversionSerializer

    def get_serializer_class(self):
        if self.action in ['create', 'update', 'partial_update']:
            return FileConversionCreateSerializer
        return FileConversionSerializer

    def get_queryset(self):
        user = self.request.user
        if user.is_authenticated:
            return FileConversion.objects.filter(created_by=user, organization=get_active_organization(self.request)).select_related("created_by")
        return FileConversion.objects.none()

    def perform_create(self, serializer):
        serializer.save(created_by=self.request.user, organization=get_active_organization(self.request))


class CurrencyHistoryView(APIView):
    """1-year exchange rate history for a base→quote pair, cached in DB."""
    permission_classes = [IsAuthenticated]

    FRANKFURTER = "https://api.frankfurter.app"
    SUPPORTED = {"USD","EUR","GBP","JPY","AUD","CAD","CHF","CNY","HKD","SGD","IDR","MYR","THB","PHP","INR","KRW","NZD","SEK","NOK","DKK","ZAR","TRY","BRL","MXN","PLN","CZK","HUF","ILS","BGN","RON","ISK"}

    def get(self, request):
        base = (request.query_params.get("from") or "").upper().strip()
        quote = (request.query_params.get("to") or "").upper().strip()
        if base not in self.SUPPORTED or quote not in self.SUPPORTED:
            return Response({"error": "Unsupported or missing currency code."}, status=400)
        if base == quote:
            return Response({"base": base, "quote": quote, "series": []})

        end = date.today()
        start = end - timedelta(days=365)

        qs = CurrencyRate.objects.filter(base=base, quote=quote, date__gte=start, date__lte=end)
        latest = qs.order_by("-date").values_list("date", flat=True).first()
        if latest is None or latest < end - timedelta(days=1):
            self._refresh(base, quote, start, end)
            qs = CurrencyRate.objects.filter(base=base, quote=quote, date__gte=start, date__lte=end)

        series = [{"date": r.date.isoformat(), "rate": float(r.rate)} for r in qs.order_by("date")]
        return Response({"base": base, "quote": quote, "series": series})

    def _refresh(self, base, quote, start, end):
        url = f"{self.FRANKFURTER}/{start.isoformat()}..{end.isoformat()}"
        try:
            r = requests.get(url, params={"from": base, "to": quote}, timeout=15)
            r.raise_for_status()
            data = r.json()
        except Exception:
            return
        rates = data.get("rates") or {}
        objs = []
        for d_iso, kv in rates.items():
            v = kv.get(quote)
            if v is None:
                continue
            objs.append(CurrencyRate(base=base, quote=quote, date=d_iso, rate=v))
        if objs:
            CurrencyRate.objects.bulk_create(objs, ignore_conflicts=True)


class ScribdScrapeViewSet(viewsets.ModelViewSet):
    """Submit a Scribd URL and get back a PDF/TXT file with the document content."""
    queryset = ScribdScrape.objects.select_related("created_by")
    permission_classes = [IsAuthenticated]

    def get_serializer_class(self):
        if self.action == "create":
            return ScribdScrapeCreateSerializer
        return ScribdScrapeSerializer

    def get_queryset(self):
        return ScribdScrape.objects.filter(created_by=self.request.user, organization=get_active_organization(self.request))

    def perform_create(self, serializer):
        from .scribd import scrape, extract_doc_id, sanitize_filename
        from pathlib import Path
        from django.http import HttpResponse

        validated = serializer.validated_data
        url = validated["scribd_url"]
        output_format = validated.get("output_format", "pdf")
        doc_id = extract_doc_id(url) or ""

        scrape_obj = serializer.save(
            created_by=self.request.user,
            organization=get_active_organization(self.request),
            scribd_doc_id=doc_id,
            status="running",
            output_format=output_format,
        )

        result = scrape(url)
        self._result = result
        self._scrape_obj = scrape_obj

    def create(self, request, *args, **kwargs):
        from django.http import HttpResponse
        from pathlib import Path

        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        validated = serializer.validated_data
        url = validated["scribd_url"]
        output_format = validated.get("output_format", "pdf")

        from .scribd import scrape, extract_doc_id
        doc_id = extract_doc_id(url) or ""

        scrape_obj = ScribdScrape.objects.create(
            scribd_url=url,
            scribd_doc_id=doc_id,
            status="running",
            output_format=output_format,
            created_by=request.user,
            organization=get_active_organization(request),
        )

        result = scrape(url)

        if "error" in result:
            scrape_obj.status = "failed"
            scrape_obj.error_message = result["error"]
            scrape_obj.save(update_fields=["status", "error_message"])
            return Response({"error": result["error"]}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)

        filepath = Path(result["path"])
        if not filepath.exists():
            scrape_obj.status = "failed"
            scrape_obj.error_message = "Output file not found"
            scrape_obj.save(update_fields=["status", "error_message"])
            return Response({"error": "Output file not found"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        scrape_obj.status = "completed"
        scrape_obj.title = result.get("title", filepath.stem)
        scrape_obj.strategy_used = result.get("strategy", "")
        scrape_obj.file_size = filepath.stat().st_size
        scrape_obj.save(update_fields=["status", "title", "strategy_used", "file_size"])

        content_type = "application/pdf" if filepath.suffix == ".pdf" else "text/plain"
        ext = filepath.suffix

        from .scribd import sanitize_filename
        filename = f"{sanitize_filename(scrape_obj.title or 'document')}{ext}"

        try:
            file_content = filepath.read_bytes()
        finally:
            try:
                filepath.unlink()
            except Exception:
                pass

        response = HttpResponse(file_content, content_type=content_type)
        response["Content-Disposition"] = f'attachment; filename="{filename}"'
        return response

