"""
Auth Views — Email login, phone OTP, profile, avatar.
"""
import logging
from io import BytesIO

from django.core.files.uploadedfile import InMemoryUploadedFile
from django.db import OperationalError
from django.utils import timezone
from rest_framework import filters, generics, permissions, serializers, status, viewsets
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken

from .geoip_service import detect_country_for_request
from .models import User
from .otp_service import OTPService
from .serializers import (
    AuthSettingsSerializer,
    LinkPhoneSerializer,
    LoginSerializer,
    PhoneRegisterSerializer,
    ProfileUpdateSerializer,
    RegisterSerializer,
    SendOTPSerializer,
    UserSerializer,
    VerifyOTPSerializer,
    UserManagementListSerializer,
    UserManagementDetailSerializer,
    UserCreateByAdminSerializer,
    UserUpdateByAdminSerializer,
)

logger = logging.getLogger('apps')


def _get_client_ip(request) -> str | None:
    x_forwarded = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded:
        return x_forwarded.split(',')[0].strip()
    return request.META.get('REMOTE_ADDR')


def _get_auth_settings(request) -> dict:
    """دریافت تنظیمات auth از tenant یا مقادیر پیش‌فرض."""
    defaults = {
        'allow_mobile_auth': True,
        'allow_email_auth': True,
        'default_auth_method': 'mobile',
        'default_country': 'IR',
        'otp_length': 5,
        'otp_expire_seconds': 120,
        'otp_max_attempts': 5,
        'otp_retry_delay_seconds': 60,
    }
    tenant = getattr(request, 'tenant', None)
    if tenant:
        tenant_settings = getattr(tenant, 'settings', {})
        if isinstance(tenant_settings, dict):
            auth_settings = tenant_settings.get('auth', {})
            defaults.update(auth_settings)
    return defaults


# ─── Email Auth ──────────────────────────────────────────────────────────


class RegisterView(generics.CreateAPIView):
    """User registration endpoint — email based."""
    queryset = User.objects.all()
    permission_classes = [permissions.AllowAny]
    serializer_class = RegisterSerializer


class LoginView(APIView):
    """User login endpoint — email + password."""
    permission_classes = [permissions.AllowAny]
    
    def post(self, request):
        try:
            serializer = LoginSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            user = serializer.validated_data['user']
            
            refresh = RefreshToken.for_user(user)
            
            user.last_login = timezone.now()
            user.save(update_fields=['last_login'])
            
            return Response({
                'user': UserSerializer(user).data,
                'tokens': {
                    'refresh': str(refresh),
                    'access': str(refresh.access_token),
                }
            })
        except serializers.ValidationError as e:
            logger.info('Login validation failed for %s: %s', request.data.get('email', 'unknown'), e.detail)
            return Response(
                e.detail if isinstance(e.detail, dict) else {'non_field_errors': e.detail if isinstance(e.detail, list) else [str(e.detail)]},
                status=status.HTTP_400_BAD_REQUEST,
            )
        except OperationalError as e:
            logger.error('Database connection failed during login: %s', e)
            return Response(
                {'error': 'Database connection failed. Please ensure the database server is running.'},
                status=status.HTTP_503_SERVICE_UNAVAILABLE,
            )
        except Exception as e:
            logger.error('Unexpected error during login: %s', e, exc_info=True)
            return Response(
                {'error': 'An unexpected error occurred. Please try again later.'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )


class LogoutView(APIView):
    """User logout endpoint."""
    permission_classes = [permissions.IsAuthenticated]
    
    def post(self, request):
        try:
            refresh_token = request.data.get('refresh')
            if refresh_token:
                try:
                    token = RefreshToken(refresh_token)
                    token.blacklist()
                except Exception:
                    pass
            return Response({'message': 'Successfully logged out'}, status=status.HTTP_200_OK)
        except Exception:
            return Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)


# ─── OTP Auth ────────────────────────────────────────────────────────────


class SendOTPView(APIView):
    """ارسال کد OTP به شماره موبایل."""
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = SendOTPSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        auth_settings = _get_auth_settings(request)
        if not auth_settings.get('allow_mobile_auth', True):
            return Response(
                {'error': {'code': 'mobile_auth_disabled', 'message': 'احراز هویت با موبایل غیرفعال است'}},
                status=status.HTTP_403_FORBIDDEN,
            )

        ip = _get_client_ip(request)
        result = OTPService.send_otp(
            phone=serializer.validated_data['phone'],
            ip_address=ip,
            tenant_settings=auth_settings,
        )

        if result['success']:
            return Response({
                'data': {
                    'message': result['message'],
                    'retry_after': result.get('retry_after', 60),
                    'expires_in': result.get('expires_in', 120),
                }
            })
        return Response(
            {'error': {'code': 'otp_send_failed', 'message': result['message']}},
            status=status.HTTP_429_TOO_MANY_REQUESTS if 'retry_after' in result else status.HTTP_400_BAD_REQUEST,
        )


class VerifyOTPView(APIView):
    """تأیید OTP و ورود/ثبت‌نام خودکار."""
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = VerifyOTPSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        phone = OTPService.normalize_phone(serializer.validated_data['phone'])
        code = serializer.validated_data['code']

        result = OTPService.verify_otp(phone=phone, code=code)
        if not result['success']:
            return Response(
                {'error': {'code': 'otp_invalid', 'message': result['message']}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Find or create user
        user = User.objects.filter(phone=phone).first()
        is_new = False
        if not user:
            user = User.objects.create_user(
                phone=phone,
                phone_verified_at=timezone.now(),
                phone_country_code='+98',
            )
            is_new = True
        elif not user.phone_verified_at:
            user.phone_verified_at = timezone.now()
            user.save(update_fields=['phone_verified_at'])

        user.last_login = timezone.now()
        user.save(update_fields=['last_login'])

        refresh = RefreshToken.for_user(user)

        # Audit log
        try:
            from apps.services.audit_log.services import AuditLogService
            from apps.services.audit_log.models import AuditAction
            AuditLogService.log(
                action=AuditAction.LOGIN,
                resource_type='User',
                resource_id=str(user.pk),
                user=user,
                module='auth',
                ip_address=_get_client_ip(request),
                user_agent=request.META.get('HTTP_USER_AGENT', ''),
                metadata={'method': 'phone_otp', 'is_new_user': is_new},
            )
        except Exception:
            pass

        return Response({
            'data': {
                'user': UserSerializer(user).data,
                'tokens': {
                    'refresh': str(refresh),
                    'access': str(refresh.access_token),
                },
                'is_new_user': is_new,
            }
        })


class ResendOTPView(APIView):
    """ارسال مجدد OTP."""
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = SendOTPSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        auth_settings = _get_auth_settings(request)
        ip = _get_client_ip(request)
        result = OTPService.send_otp(
            phone=serializer.validated_data['phone'],
            ip_address=ip,
            tenant_settings=auth_settings,
        )

        if result['success']:
            return Response({
                'data': {
                    'message': result['message'],
                    'retry_after': result.get('retry_after', 60),
                }
            })
        return Response(
            {'error': {'code': 'otp_resend_failed', 'message': result['message']}},
            status=status.HTTP_429_TOO_MANY_REQUESTS if 'retry_after' in result else status.HTTP_400_BAD_REQUEST,
        )


class LinkPhoneView(APIView):
    """اتصال شماره موبایل به حساب موجود."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        serializer = LinkPhoneSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        phone = OTPService.normalize_phone(serializer.validated_data['phone'])
        code = serializer.validated_data['code']

        # Check if phone already taken
        if User.objects.filter(phone=phone).exclude(pk=request.user.pk).exists():
            return Response(
                {'error': {'code': 'phone_taken', 'message': 'این شماره قبلاً ثبت شده است'}},
                status=status.HTTP_409_CONFLICT,
            )

        result = OTPService.verify_otp(phone=phone, code=code)
        if not result['success']:
            return Response(
                {'error': {'code': 'otp_invalid', 'message': result['message']}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        request.user.phone = phone
        request.user.phone_verified_at = timezone.now()
        request.user.phone_country_code = '+98'
        request.user.save(update_fields=['phone', 'phone_verified_at', 'phone_country_code'])

        return Response({'data': UserSerializer(request.user).data})


# ─── Auth Settings ───────────────────────────────────────────────────────


class AuthSettingsView(APIView):
    """تنظیمات عمومی auth — public."""
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        settings_data = _get_auth_settings(request)
        serializer = AuthSettingsSerializer(settings_data)
        return Response({'data': serializer.data})


class DetectCountryView(APIView):
    """تشخیص کشور از IP."""
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        country_code = detect_country_for_request(request)
        # Fetch country from localization
        country_data = {'iso_code': country_code}
        try:
            from apps.core.localization.models import Country
            country = Country.objects.filter(iso2=country_code).first()
            if country:
                country_data = {
                    'iso_code': country.iso2,
                    'name': country.name,
                    'name_en': country.name_en,
                    'phone_code': country.phone_code,
                    'flag_emoji': country.flag_emoji,
                }
        except Exception:
            pass
        return Response({'data': country_data})


# ─── Profile ─────────────────────────────────────────────────────────────


class UserProfileView(APIView):
    """User profile endpoint — GET/PUT/PATCH."""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        serializer = UserSerializer(request.user)
        return Response({'data': serializer.data})

    def put(self, request):
        return self._update(request)

    def patch(self, request):
        return self._update(request, partial=True)

    def _update(self, request, partial=False):
        old_data = ProfileUpdateSerializer(request.user).data
        serializer = ProfileUpdateSerializer(
            request.user, data=request.data, partial=partial
        )
        serializer.is_valid(raise_exception=True)
        serializer.save()

        # Audit log
        try:
            from apps.services.audit_log.services import AuditLogService
            from apps.services.audit_log.models import AuditAction
            changes = AuditLogService.compute_changes(old_data, serializer.data)
            if changes:
                AuditLogService.log_model_change(
                    instance=request.user,
                    action=AuditAction.UPDATE,
                    changes=changes,
                    user=request.user,
                    module='auth',
                    request=request,
                )
        except Exception:
            pass

        return Response({'data': UserSerializer(request.user).data})


class AvatarUploadView(APIView):
    """آپلود و پردازش آواتار — crop/resize به 64x64."""
    permission_classes = [permissions.IsAuthenticated]
    parser_classes = [MultiPartParser]

    def post(self, request):
        file = request.FILES.get('avatar')
        if not file:
            return Response(
                {'error': {'code': 'no_file', 'message': 'فایلی انتخاب نشده است'}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Validate size (max 5MB)
        if file.size > 5 * 1024 * 1024:
            return Response(
                {'error': {'code': 'file_too_large', 'message': 'حداکثر حجم فایل ۵ مگابایت است'}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Process image
        try:
            from PIL import Image

            img = Image.open(file)
            img = img.convert('RGB')

            # Center crop to square
            width, height = img.size
            min_dim = min(width, height)
            left = (width - min_dim) // 2
            top = (height - min_dim) // 2
            img = img.crop((left, top, left + min_dim, top + min_dim))

            # Resize to 64x64
            img = img.resize((64, 64), Image.LANCZOS)

            # Save to buffer
            buffer = BytesIO()
            img.save(buffer, format='JPEG', quality=85)
            buffer.seek(0)

            # Delete old avatar
            if request.user.avatar:
                request.user.avatar.delete(save=False)

            # Save new avatar
            filename = f"avatar_{request.user.pk}.jpg"
            request.user.avatar = InMemoryUploadedFile(
                file=buffer,
                field_name='avatar',
                name=filename,
                content_type='image/jpeg',
                size=buffer.getbuffer().nbytes,
                charset=None,
            )
            request.user.save(update_fields=['avatar'])

            return Response({'data': {'avatar': request.user.avatar.url}})

        except Exception as e:
            logger.error(f"Avatar processing error: {e}", exc_info=True)
            return Response(
                {'error': {'code': 'processing_error', 'message': 'خطا در پردازش تصویر'}},
                status=status.HTTP_400_BAD_REQUEST,
            )

    def delete(self, request):
        if request.user.avatar:
            request.user.avatar.delete(save=False)
            request.user.avatar = None
            request.user.save(update_fields=['avatar'])
        return Response({'data': {'message': 'آواتار حذف شد'}})


class CoverUploadView(APIView):
    """آپلود تصویر کاور."""
    permission_classes = [permissions.IsAuthenticated]
    parser_classes = [MultiPartParser]

    def post(self, request):
        file = request.FILES.get('cover')
        if not file:
            return Response(
                {'error': {'code': 'no_file', 'message': 'فایلی انتخاب نشده است'}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if file.size > 5 * 1024 * 1024:
            return Response(
                {'error': {'code': 'file_too_large', 'message': 'حداکثر حجم فایل ۵ مگابایت است'}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if request.user.cover_image:
            request.user.cover_image.delete(save=False)

        request.user.cover_image = file
        request.user.save(update_fields=['cover_image'])

        return Response({'data': {'cover_image': request.user.cover_image.url}})

    def delete(self, request):
        if request.user.cover_image:
            request.user.cover_image.delete(save=False)
            request.user.cover_image = None
            request.user.save(update_fields=['cover_image'])
        return Response({'data': {'message': 'کاور حذف شد'}})


class ProfileHistoryView(APIView):
    """تاریخچه تغییرات پروفایل."""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        try:
            from apps.services.audit_log.services import AuditLogService
            logs = AuditLogService.get_resource_history(
                resource_type='User',
                resource_id=str(request.user.pk),
            )[:50]
            from apps.services.audit_log.serializers import AuditLogListSerializer
            serializer = AuditLogListSerializer(logs, many=True)
            return Response({'data': serializer.data})
        except Exception:
            return Response({'data': []})


# ═══════════════════════════════════════════════════
# User List — lookup endpoint for select fields
# ═══════════════════════════════════════════════════

class UserListSerializer(serializers.ModelSerializer):
    """سریالایزر ساده برای لیست کاربران (lookup)."""
    full_name = serializers.CharField(read_only=True)

    class Meta:
        model = User
        fields = ['id', 'full_name', 'email']
        read_only_fields = fields


class UserListView(generics.ListAPIView):
    """لیست کاربران — برای استفاده در فیلدهای lookup فرم‌ها."""
    serializer_class = UserListSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        qs = User.objects.filter(is_active=True).order_by('first_name', 'last_name')
        # Filter by tenant if available (skip if tenant is public/default)
        tenant = getattr(self.request, 'tenant', None)
        if tenant and hasattr(tenant, 'schema_name') and tenant.schema_name != 'public':
            qs = qs.filter(tenant=tenant)
        # Search support
        search = self.request.query_params.get('search', '')
        if search:
            from django.db.models import Q
            qs = qs.filter(
                Q(first_name__icontains=search) |
                Q(last_name__icontains=search) |
                Q(email__icontains=search)
            )
        return qs


# ═══════════════════════════════════════════════════
# User Management — tenant admin CRUD
# ═══════════════════════════════════════════════════

class UserManagementViewSet(viewsets.ModelViewSet):
    """
    مدیریت کاربران tenant.

    list:    لیست کاربران tenant
    create:  ایجاد کاربر جدید
    retrieve: جزئیات کاربر
    update:  بروزرسانی کاربر
    partial_update: بروزرسانی جزئی
    destroy: غیرفعال‌سازی (soft delete)
    """
    permission_classes = [permissions.IsAuthenticated, permissions.IsAdminUser]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['first_name', 'last_name', 'email', 'phone']
    ordering_fields = ['first_name', 'last_name', 'email', 'created_at', 'last_login_at']
    ordering = ['-created_at']

    def get_queryset(self):
        qs = User.objects.select_related('hrm_employee').order_by('-created_at')
        tenant = getattr(self.request, 'tenant', None)
        if tenant and hasattr(tenant, 'schema_name') and tenant.schema_name != 'public':
            qs = qs.filter(tenant=tenant)

        # Filter support
        is_active = self.request.query_params.get('is_active')
        if is_active is not None:
            qs = qs.filter(is_active=is_active.lower() in ('true', '1'))

        return qs

    def get_serializer_class(self):
        if self.action == 'list':
            return UserManagementListSerializer
        if self.action in ('retrieve',):
            return UserManagementDetailSerializer
        if self.action == 'create':
            return UserCreateByAdminSerializer
        if self.action in ('update', 'partial_update'):
            return UserUpdateByAdminSerializer
        return UserManagementDetailSerializer

    def perform_create(self, serializer):
        tenant = getattr(self.request, 'tenant', None)
        user = serializer.save()
        if tenant and not user.tenant:
            user.tenant = tenant
            user.save(update_fields=['tenant'])

    def perform_destroy(self, instance):
        """Soft delete — فقط غیرفعال‌سازی."""
        instance.is_active = False
        instance.save(update_fields=['is_active'])

    @action(detail=True, methods=['post'], url_path='activate')
    def activate(self, request, pk=None):
        """فعال‌سازی مجدد کاربر."""
        user = self.get_object()
        user.is_active = True
        user.save(update_fields=['is_active'])
        return Response(UserManagementDetailSerializer(user).data)

    @action(detail=True, methods=['post'], url_path='reset-password')
    def reset_password(self, request, pk=None):
        """تنظیم رمز عبور جدید توسط ادمین."""
        user = self.get_object()
        password = request.data.get('password')
        if not password or len(password) < 8:
            return Response(
                {'error': {'code': 'invalid_password', 'message': 'رمز عبور باید حداقل ۸ کاراکتر باشد.'}},
                status=status.HTTP_400_BAD_REQUEST,
            )
        user.set_password(password)
        user.save(update_fields=['password'])
        return Response({'data': {'message': 'رمز عبور با موفقیت تغییر یافت.'}})

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        detail = UserManagementDetailSerializer(serializer.instance)
        return Response({'data': detail.data}, status=status.HTTP_201_CREATED)

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        serializer = self.get_serializer(queryset, many=True)
        return Response({'data': serializer.data})

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response({'data': serializer.data})

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        detail = UserManagementDetailSerializer(instance)
        return Response({'data': detail.data})

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response({'data': {'message': 'کاربر غیرفعال شد.'}}, status=status.HTTP_200_OK)
