"""Auth API: signup, login, logout, forgot-password, refresh-token."""
import datetime

from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.permissions import AllowAny

from core.models import AppUser, PasswordResetCode
from .auth_utils import (
    check_password, decode_token, hash_password, make_access_token,
    make_refresh_token, make_reset_code, reset_code_expiry, ok, err,
)
from .permissions import IsAppUser


class SignupView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        d = request.data
        name = (d.get('name') or '').strip()
        email = (d.get('email') or '').strip().lower()
        password = d.get('password', '')
        phone = d.get('phone', '')
        address = d.get('address', '')
        dob = d.get('dateOfBirth') or d.get('date_of_birth')
        age = d.get('age')

        if not name or not email or not password:
            return err('name, email and password are required.', 400)
        if len(password) < 8:
            return err('Password must be at least 8 characters.', 400)
        if AppUser.objects.filter(email=email).exists():
            return err('Email is already registered.', 409)

        user = AppUser(
            name=name, email=email,
            password=hash_password(password),
            phone=phone, address=address,
            date_of_birth=dob or None,
            age=age or None,
            role='user', status='active',
            created_at=timezone.now(), updated_at=timezone.now(),
        )
        user.save()

        token = make_access_token(user.id, user.role)
        refresh = make_refresh_token(user.id, user.role)
        return ok({'token': token, 'refreshToken': refresh,
                   'user': _user_dict(user)}, 'Account created.', 201)


class LoginView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        email = (request.data.get('email') or '').strip().lower()
        password = request.data.get('password', '')

        if not email or not password:
            return err('Email and password required.', 400)

        try:
            user = AppUser.objects.get(email=email)
        except AppUser.DoesNotExist:
            return err('Invalid email or password.', 401)

        if user.status == 'banned':
            return err('Account is banned.', 403)
        if not check_password(password, user.password):
            return err('Invalid email or password.', 401)

        token = make_access_token(user.id, user.role)
        refresh = make_refresh_token(user.id, user.role)
        return ok({'token': token, 'refreshToken': refresh,
                   'user': _user_dict(user)}, 'Login successful.')


class RefreshTokenView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        raw = request.data.get('refreshToken') or request.data.get('refresh_token', '')
        payload = decode_token(raw)
        if not payload or payload.get('type') != 'refresh':
            return err('Invalid or expired refresh token.', 401)
        try:
            user = AppUser.objects.get(id=payload['sub'], status='active')
        except AppUser.DoesNotExist:
            return err('User not found.', 401)
        return ok({
            'token': make_access_token(user.id, user.role),
            'refreshToken': make_refresh_token(user.id, user.role),
        })


class LogoutView(APIView):
    permission_classes = [IsAppUser]

    def post(self, request):
        # Stateless JWT — nothing to invalidate server-side.
        return ok(message='Logged out.')


class ForgotPasswordView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        email = (request.data.get('email') or '').strip().lower()
        if not email:
            return err('Email is required.', 400)
        if not AppUser.objects.filter(email=email).exists():
            # Don't reveal whether email exists
            return ok(message='If that email is registered you will receive a code.')

        code = make_reset_code()
        expires = reset_code_expiry()
        PasswordResetCode.objects.filter(email=email, used=False).update(used=True)
        PasswordResetCode.objects.create(email=email, code=code, expires_at=expires)

        # TODO: Send email via SMTP when configured.
        # For now, log to Django console.
        print(f'[FORGOT-PASSWORD] Code for {email}: {code}')

        return ok(message='Reset code sent. Check your email.')


class VerifyResetCodeView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        email = (request.data.get('email') or '').strip().lower()
        code = (request.data.get('code') or '').strip()
        new_password = request.data.get('newPassword') or request.data.get('new_password', '')

        if not email or not code or not new_password:
            return err('email, code and newPassword are required.', 400)
        if len(new_password) < 8:
            return err('Password must be at least 8 characters.', 400)

        now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)
        try:
            reset = PasswordResetCode.objects.get(
                email=email, code=code, used=False, expires_at__gt=now,
            )
        except PasswordResetCode.DoesNotExist:
            return err('Invalid or expired code.', 400)

        AppUser.objects.filter(email=email).update(
            password=hash_password(new_password),
            updated_at=timezone.now(),
        )
        reset.used = True
        reset.save()
        return ok(message='Password reset successfully.')


# ── helpers ───────────────────────────────────────────────────────────────────

def _user_dict(user: AppUser) -> dict:
    return {
        'id': user.id, 'name': user.name, 'email': user.email,
        'phone': user.phone, 'role': user.role, 'status': user.status,
        'profile_image': user.profile_image, 'bio': user.bio,
        'is_pregnant': user.is_pregnant,
        'cycle_length_days': user.cycle_length_days,
        'last_period_date': str(user.last_period_date) if user.last_period_date else None,
    }
