# views.py
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework import viewsets, permissions, status, filters
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.exceptions import TokenError
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.decorators import action
from django.conf import settings as django_settings
from django.db.models import Q
from django.utils import timezone
import logging
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import json
import smtplib
from .serializers import MasterAdminTokenSerializer
from .models import (
    School,
    SchoolType,
    SchoolBoard,
    Country,
    State,
    City,
    Area,
    Pincode,
)
from .models import EmailTemplate, BulkEmailLog
from .serializers import (
    EmailTemplateSerializer,
    EmailTemplateListSerializer,
    BulkEmailLogSerializer,
    SendBulkEmailSerializer,
)

from .serializers import (
    SchoolSerializer,
    SchoolTypeSerializer,
    SchoolBoardSerializer,
    CurrentUserSerializer,
)

from .location_serializers import (
    CountrySerializer,
    StateSerializer,
    PincodeSerializer,
    AreaSerializer,
    CitySerializer,
)

logger = logging.getLogger(__name__)


# ==============================#
# AUTHENTICATION VIEWS          #
# ==============================#


class MasterAdminLoginView(TokenObtainPairView):
    """
    Login view that returns both access and refresh tokens in response body.
    Use this if you want tokens in JSON response (not in cookies).
    """

    serializer_class = MasterAdminTokenSerializer


class MyTokenObtainPairView(TokenObtainPairView):
    """
    Login view that sets refresh token in HTTP-only cookie and
    returns only access token and user info in response body.
    """

    serializer_class = MasterAdminTokenSerializer

    def post(self, request, *args, **kwargs):
        response = super().post(request, *args, **kwargs)

        refresh = response.data.get("refresh")

        if refresh:
            response.set_cookie(
                key="refresh_token",
                value=refresh,
                httponly=True,
                secure=True,
                samesite="Lax",
                max_age=7 * 24 * 60 * 60,
                path="/",
            )
            del response.data["refresh"]

        return response


class RefreshTokenView(APIView):
    """
    Custom refresh token view that reads refresh token from HTTP-only cookie.
    """

    def post(self, request):
        refresh_token = request.COOKIES.get("refresh_token")

        if not refresh_token:
            return Response(
                {"error": "Refresh token is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            refresh = RefreshToken(refresh_token)
            new_access = str(refresh.access_token)

            return Response({"access": new_access}, status=status.HTTP_200_OK)

        except TokenError:
            return Response(
                {"error": "Invalid or expired refresh token"},
                status=status.HTTP_401_UNAUTHORIZED,
            )


class LogoutView(APIView):
    """
    Logout view to clear refresh token cookie.
    """

    def post(self, request):
        response = Response(
            {"message": "Successfully logged out"}, status=status.HTTP_200_OK
        )
        response.delete_cookie(key="refresh_token", path="/", samesite="Lax")
        return response


# In your people/views.py, add this view:


class CurrentUserDetailView(APIView):
    """
    API endpoint that returns details of the currently authenticated user.
    Fetches data from both master database and school database.
    """

    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        """
        Get current user details with multi-database support
        """
        try:
            # Log the request for debugging
            logger.info(f"/me endpoint called for user: {request.user.id}")
            logger.info(f"Request user: {request.user.email}")
            logger.info(f"School context: {getattr(request, 'school', None)}")

            # Initialize response data
            response_data = {}

            # 1. Get User from master database
            try:
                # The request.user should already be from master database if your authentication
                # middleware is set up correctly
                master_user = request.user

                # If you need to explicitly get from master database:
                # master_user = User.objects.using('default').get(id=request.user.id)

                response_data["user"] = master_user
                logger.info(f"✅ Found master user: {master_user.email}")
            except Exception as e:
                logger.error(f"❌ Error getting master user: {str(e)}")
                return Response(
                    {"error": "User not found in master database"},
                    status=status.HTTP_404_NOT_FOUND,
                )

            # 2. Get School from master database
            try:
                # The school should be in the request if your middleware sets it
                school = getattr(request, "school", None)

                if not school and hasattr(request, "school_id"):
                    # If school_id is in request but school object isn't
                    from master_admin.models import School

                    school = School.objects.using("default").get(id=request.school_id)
                    logger.info(f"✅ Found school by ID: {school.name}")

                response_data["school"] = school
                if school:
                    logger.info(f"✅ Found school: {school.name}")
                else:
                    logger.warning("⚠️ No school found in request")
            except Exception as e:
                logger.error(f"❌ Error getting school: {str(e)}")
                response_data["school"] = None

            # 3. Get UserProfile from master database
            try:
                from master_admin.models import UserProfile

                # Try to get UserProfile for this user and school
                user_profile = None
                if school:
                    user_profile = (
                        UserProfile.objects.using("default")
                        .filter(user=master_user, school=school, is_active=True)
                        .first()
                    )

                    if not user_profile:
                        # Try without school filter (in case school context is wrong)
                        user_profile = (
                            UserProfile.objects.using("default")
                            .filter(user=master_user, is_active=True)
                            .first()
                        )

                response_data["user_profile"] = user_profile
                if user_profile:
                    logger.info(f"✅ Found user profile: {user_profile.id}")
                    logger.info(f"User profile metadata: {user_profile.metadata}")
                else:
                    logger.warning("⚠️ No user profile found")
            except ImportError:
                logger.warning("⚠️ UserProfile model not available")
                response_data["user_profile"] = None
            except Exception as e:
                logger.error(f"❌ Error getting user profile: {str(e)}")
                response_data["user_profile"] = None

            # 4. Check if we should query school database
            # Only query school database if we have a user_profile with metadata
            user_profile = response_data.get("user_profile")
            if user_profile and user_profile.metadata:
                # The database router should automatically route to school database
                # based on the school context, but we can force it if needed

                # Note: The following lookups will automatically use the school database
                # due to your database router when accessed through the school context

                # We don't need to explicitly query here because the serializer
                # will handle it with the proper database routing
                pass

            # 5. Serialize the response
            serializer = CurrentUserSerializer(
                response_data, context={"request": request}
            )

            # 6. Add additional context if needed
            data = serializer.data

            # Add enabled modules for this school
            try:
                from .models import SchoolModuleAccess, SchoolModule
                school_obj = response_data.get("school")
                if school_obj:
                    assigned = list(
                        SchoolModuleAccess.objects.using("default")
                        .filter(school=school_obj, is_enabled=True)
                        .values_list("module__code", flat=True)
                    )
                    defaults = list(
                        SchoolModule.objects.using("default")
                        .filter(is_default=True, is_active=True)
                        .values_list("code", flat=True)
                    )
                    data["enabled_modules"] = list(set(assigned + defaults))
                else:
                    data["enabled_modules"] = []
            except Exception:
                data["enabled_modules"] = []

            # Add authentication token info if using token auth
            if hasattr(request, "auth"):
                data["token_info"] = {
                    "token_type": getattr(request.auth, "token_type", "Bearer"),
                    "expires_at": getattr(request.auth, "expires_at", None),
                }

            # Add request metadata
            data["request_info"] = {
                "ip_address": request.META.get("REMOTE_ADDR"),
                "user_agent": request.META.get("HTTP_USER_AGENT"),
                "timestamp": timezone.now().isoformat(),
            }

            logger.info(
                f"✅ /me endpoint completed successfully for user: {master_user.email}"
            )

            return Response(data)

        except Exception as e:
            logger.error(f"❌ Error in /me endpoint: {str(e)}", exc_info=True)
            return Response(
                {"error": "Failed to fetch user details", "detail": str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

    def put(self, request):
        """
        Update current user profile
        """
        try:
            # Get current user data
            master_user = request.user

            # Get user profile
            from master_admin.models import UserProfile, School

            school = getattr(request, "school", None)
            if not school:
                return Response(
                    {"error": "School context not found"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            user_profile = (
                UserProfile.objects.using("default")
                .filter(user=master_user, school=school, is_active=True)
                .first()
            )

            if not user_profile:
                return Response(
                    {"error": "User profile not found"},
                    status=status.HTTP_404_NOT_FOUND,
                )

            # Update user data if provided
            user_data = {}
            if "first_name" in request.data:
                user_data["first_name"] = request.data["first_name"]
            if "last_name" in request.data:
                user_data["last_name"] = request.data["last_name"]
            if "email" in request.data:
                # Check if email is already taken
                if (
                    User.objects.using("default")
                    .filter(email=request.data["email"])
                    .exclude(id=master_user.id)
                    .exists()
                ):
                    return Response(
                        {"error": "Email already in use"},
                        status=status.HTTP_400_BAD_REQUEST,
                    )
                user_data["email"] = request.data["email"]
                user_data["username"] = request.data["email"]

            # Update user
            if user_data:
                for key, value in user_data.items():
                    setattr(master_user, key, value)
                master_user.save(using="default")

            # Update user profile data
            profile_data = {}
            if "language" in request.data:
                profile_data["language"] = request.data["language"]
            if "timezone" in request.data:
                profile_data["timezone"] = request.data["timezone"]
            if "email_notifications" in request.data:
                profile_data["email_notifications"] = request.data[
                    "email_notifications"
                ]
            if "sms_notifications" in request.data:
                profile_data["sms_notifications"] = request.data["sms_notifications"]

            # Update user profile
            if profile_data:
                for key, value in profile_data.items():
                    setattr(user_profile, key, value)
                user_profile.save(using="default")

            # Update school-specific profile based on role
            role = user_profile.metadata.get("role") if user_profile.metadata else None

            if role == "TEACHER" and "external_teacher_id" in user_profile.metadata:
                try:
                    teacher_id = user_profile.metadata["external_teacher_id"]
                    teacher = Teacher.objects.get(id=teacher_id, is_active=True)

                    teacher_data = {}
                    if "phone" in request.data:
                        teacher_data["phone"] = request.data["phone"]
                    if "qualification" in request.data:
                        teacher_data["qualification"] = request.data["qualification"]

                    if teacher_data:
                        for key, value in teacher_data.items():
                            setattr(teacher, key, value)
                        teacher.save()
                except Teacher.DoesNotExist:
                    pass

            elif role == "STUDENT" and "external_student_id" in user_profile.metadata:
                try:
                    student_id = user_profile.metadata["external_student_id"]
                    student = Student.objects.get(id=student_id, is_active=True)

                    student_data = {}
                    if "phone" in request.data:
                        student_data["phone"] = request.data["phone"]

                    if student_data:
                        for key, value in student_data.items():
                            setattr(student, key, value)
                        student.save()
                except Student.DoesNotExist:
                    pass

            elif role == "PARENT" and "external_parent_id" in user_profile.metadata:
                try:
                    parent_id = user_profile.metadata["external_parent_id"]
                    parent = Parent.objects.get(id=parent_id, is_active=True)

                    parent_data = {}
                    if "phone" in request.data:
                        parent_data["phone"] = request.data["phone"]
                    if "occupation" in request.data:
                        parent_data["occupation"] = request.data["occupation"]

                    if parent_data:
                        for key, value in parent_data.items():
                            setattr(parent, key, value)
                        parent.save()
                except Parent.DoesNotExist:
                    pass

            # Return updated data
            response_data = {
                "user": master_user,
                "school": school,
                "user_profile": user_profile,
            }

            serializer = CurrentUserSerializer(
                response_data, context={"request": request}
            )
            return Response(serializer.data)

        except Exception as e:
            logger.error(f"Error updating user profile: {str(e)}", exc_info=True)
            return Response(
                {"error": "Failed to update profile", "detail": str(e)},
                status=status.HTTP_400_BAD_REQUEST,
            )


class EmailTemplateViewSet(viewsets.ModelViewSet):
    """
    CRUD ViewSet for Email Templates.

    GET    /bulk-email/templates/
    POST   /bulk-email/templates/
    GET    /bulk-email/templates/{id}/
    PUT    /bulk-email/templates/{id}/
    PATCH  /bulk-email/templates/{id}/
    DELETE /bulk-email/templates/{id}/
    POST   /bulk-email/templates/{id}/preview/
    POST   /bulk-email/templates/{id}/toggle-status/
    """

    queryset = EmailTemplate.objects.all()
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = ["name", "subject", "description"]
    ordering_fields = ["name", "created_at", "updated_at", "status"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action == "list":
            return EmailTemplateListSerializer
        return EmailTemplateSerializer

    def get_queryset(self):
        qs = EmailTemplate.objects.all()
        status_filter = self.request.query_params.get("status")
        if status_filter:
            qs = qs.filter(status=status_filter)
        return qs

    def perform_create(self, serializer):
        serializer.save(created_by=self.request.user, updated_by=self.request.user)

    def perform_update(self, serializer):
        serializer.save(updated_by=self.request.user)

    @action(detail=True, methods=["post"], url_path="preview")
    def preview(self, request, pk=None):
        """Preview the rendered template with context variables."""
        template = self.get_object()
        context = request.data.get("context", {})
        rendered = template.render(context)
        return Response(
            {
                "template_id": template.id,
                "template_name": template.name,
                "variables_used": template.variables_used,
                "rendered_subject": rendered["subject"],
                "rendered_body": rendered["body"],
                "context_used": context,
            }
        )

    @action(detail=True, methods=["post"], url_path="toggle-status")
    def toggle_status(self, request, pk=None):
        """Toggle template between active/inactive."""
        template = self.get_object()
        template.status = "inactive" if template.status == "active" else "active"
        template.save()
        return Response(
            {"id": template.id, "status": template.status},
            status=status.HTTP_200_OK,
        )


# ==============================#
# SEND BULK EMAIL VIEW          #
# ==============================#


def _send_via_zepto(
    smtp_server,
    port,
    username,
    password,
    from_email,
    to_emails,
    cc_emails,
    subject,
    body,
    attachment=None,
):
    """
    Helper: send a single email via ZeptoMail SMTP.
    Optionally attaches a file (brochure, PDF, etc.).
    Raises exception on failure.
    """
    # Use 'mixed' when there's an attachment, otherwise 'alternative'
    msg = MIMEMultipart("mixed" if attachment else "alternative")
    msg["Subject"] = subject
    msg["From"] = from_email
    msg["To"] = ", ".join(to_emails)
    if cc_emails:
        msg["Cc"] = ", ".join(cc_emails)
    msg.attach(MIMEText(body, "html"))

    # ── Optional file attachment ──────────────────────────────────────────────
    if attachment:
        attachment.seek(0)  # reset pointer in case of multi-school loop
        part = MIMEBase("application", "octet-stream")
        part.set_payload(attachment.read())
        encoders.encode_base64(part)
        part.add_header(
            "Content-Disposition",
            f'attachment; filename="{attachment.name}"',
        )
        msg.attach(part)

    all_recipients = to_emails + (cc_emails if cc_emails else [])

    with smtplib.SMTP(smtp_server, port) as server:
        server.ehlo()
        server.starttls()
        server.login(username, password)
        server.sendmail(from_email, all_recipients, msg.as_string())


class SendBulkEmailView(APIView):
    """
    Send bulk emails using a template via ZeptoMail SMTP.

    Supports override_body and override_subject from the frontend rich-text editor.
    Also supports an optional file attachment (multipart/form-data).

    POST /bulk-email/send/
    JSON body (no attachment):
    {
        "template_id": 1,
        "to_emails": ["user1@example.com"],
        "cc_emails": [],
        "context": {"school_name": "School A"},
        "override_body": "<p>Hi School A,</p>...",
        "override_subject": "Hello from Dayscholor"
    }

    multipart/form-data (with attachment):
    - All above fields as form fields (context as a JSON string)
    - attachment: <file>
    """

    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        # ── Handle context being a JSON string when sent as multipart ────────
        mutable_data = request.data.copy()
        if isinstance(mutable_data.get("context"), str):
            try:
                mutable_data["context"] = json.loads(mutable_data["context"])
            except (json.JSONDecodeError, TypeError):
                mutable_data["context"] = {}

        serializer = SendBulkEmailSerializer(data=mutable_data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        data = serializer.validated_data
        template_id = data["template_id"]
        to_emails = data["to_emails"]
        cc_emails = data.get("cc_emails", [])
        context = data.get("context", {})

        # ── Read override fields from raw request.data (not validated data) ──
        override_body = request.data.get("override_body", None)
        override_subject = request.data.get("override_subject", None)

        # ── File attachment (optional) ────────────────────────────────────────
        attachment = request.FILES.get("attachment", None)

        # ── Get template ──────────────────────────────────────────────────────
        try:
            template = EmailTemplate.objects.get(id=template_id, status="active")
        except EmailTemplate.DoesNotExist:
            return Response(
                {"error": "Template not found or is not active."},
                status=status.HTTP_404_NOT_FOUND,
            )

        # ── Resolve school names ──────────────────────────────────────────────
        raw_school_name = context.get("school_name", "")
        if raw_school_name and "|||" in str(raw_school_name):
            school_names = [
                s.strip() for s in str(raw_school_name).split("|||") if s.strip()
            ]
        elif raw_school_name:
            school_names = [raw_school_name.strip()]
        else:
            school_names = [""]  # No school_name variable — send once

        # ── ZeptoMail credentials ─────────────────────────────────────────────
        smtp_server = django_settings.ZEPTO_SMTP_SERVER
        port = django_settings.ZEPTO_PORT
        username = django_settings.ZEPTO_USERNAME
        password = django_settings.ZEPTO_PASSWORD
        from_email = django_settings.ZEPTO_MARKETING_EMAIL

        # ── Send one email per school ─────────────────────────────────────────
        sent_count = 0
        failed_count = 0
        log_ids = []

        for school_name in school_names:
            current_context = {**context}
            if school_name:
                current_context["school_name"] = school_name

            # ── Use override body/subject if provided by the frontend editor ──
            if override_body or override_subject:
                final_body = override_body or ""
                final_subject = override_subject or template.subject

                # Replace {{variable}} placeholders with actual values
                for key, value in current_context.items():
                    placeholder = "{{" + key + "}}"
                    final_body = final_body.replace(placeholder, str(value))
                    final_subject = final_subject.replace(placeholder, str(value))

                rendered = {
                    "subject": final_subject,
                    "body": final_body,
                }
            else:
                # Original behaviour: render from stored template
                rendered = template.render(current_context)

            # Create log entry as pending
            log = BulkEmailLog.objects.create(
                template=template,
                template_name_snapshot=template.name,
                to_emails=", ".join(to_emails),
                cc_emails=", ".join(cc_emails) if cc_emails else None,
                rendered_subject=rendered["subject"],
                rendered_body=rendered["body"],
                context_data=current_context,
                status="pending",
                sent_by=request.user,
            )
            log_ids.append(log.id)

            # Send via ZeptoMail
            try:
                _send_via_zepto(
                    smtp_server=smtp_server,
                    port=port,
                    username=username,
                    password=password,
                    from_email=from_email,
                    to_emails=to_emails,
                    cc_emails=cc_emails if cc_emails else [],
                    subject=rendered["subject"],
                    body=rendered["body"],
                    attachment=attachment,  # ← pass file (or None)
                )

                log.status = "sent"
                log.sent_at = timezone.now()
                log.save()
                sent_count += 1

                logger.info(
                    f"✅ Email sent — school='{school_name}', "
                    f"to={to_emails}, by={request.user.email}"
                )

            except Exception as e:
                log.status = "failed"
                log.error_message = str(e)
                log.save()
                failed_count += 1

                logger.error(
                    f"❌ Email failed — school='{school_name}', error={str(e)}",
                    exc_info=True,
                )

        # ── Build response ────────────────────────────────────────────────────
        total = len(school_names)

        if sent_count == total:
            message = (
                f"All {sent_count} email(s) sent successfully."
                if total > 1
                else "Email sent successfully."
            )
            response_status = status.HTTP_200_OK
        elif sent_count > 0:
            message = f"{sent_count} of {total} email(s) sent. {failed_count} failed."
            response_status = status.HTTP_207_MULTI_STATUS
        else:
            message = f"All {total} email(s) failed to send."
            response_status = status.HTTP_500_INTERNAL_SERVER_ERROR

        return Response(
            {
                "success": sent_count > 0,
                "message": message,
                "total": total,
                "sent_count": sent_count,
                "failed_count": failed_count,
                "log_ids": log_ids,
                "recipients": {
                    "to": to_emails,
                    "cc": cc_emails,
                },
            },
            status=response_status,
        )


# ==============================#
# BULK EMAIL LOG VIEWSET        #
# ==============================#


class BulkEmailLogViewSet(viewsets.ReadOnlyModelViewSet):
    """
    Read-only ViewSet for email send logs.

    GET /bulk-email/logs/
    GET /bulk-email/logs/{id}/
    """

    queryset = BulkEmailLog.objects.select_related("template", "sent_by").all()
    serializer_class = BulkEmailLogSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = [
        "to_emails",
        "cc_emails",
        "rendered_subject",
        "template_name_snapshot",
    ]
    ordering_fields = ["created_at", "sent_at", "status"]
    ordering = ["-created_at"]

    def get_queryset(self):
        qs = BulkEmailLog.objects.select_related("template", "sent_by").all()

        status_filter = self.request.query_params.get("status")
        if status_filter:
            qs = qs.filter(status=status_filter)

        template_id = self.request.query_params.get("template_id")
        if template_id:
            qs = qs.filter(template_id=template_id)

        sent_by_me = self.request.query_params.get("sent_by_me")
        if sent_by_me == "true":
            qs = qs.filter(sent_by=self.request.user)

        date_from = self.request.query_params.get("date_from")
        date_to = self.request.query_params.get("date_to")
        if date_from:
            qs = qs.filter(created_at__date__gte=date_from)
        if date_to:
            qs = qs.filter(created_at__date__lte=date_to)

        return qs
