from __future__ import annotations

import os
import platform
from functools import wraps

from django import get_version as django_version
from django.contrib import messages
from django.contrib.auth import get_user_model, login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import (
    LoginView,
    PasswordResetCompleteView,
    PasswordResetConfirmView,
    PasswordResetDoneView,
    PasswordResetView,
)
from django.core.exceptions import PermissionDenied
from django.core.mail import send_mail
from django.db import transaction
from django.db.models import Count, Q
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.utils import timezone
from django.utils.text import slugify

from .forms import (
    BusinessOnboardingForm,
    BusinessSettingsForm,
    JobIntakeForm,
    ProfileSettingsForm,
    ProofCardForm,
    PublicFeedbackForm,
    SignUpForm,
    TeamMemberInviteForm,
    TrustForgeAuthenticationForm,
    TrustForgePasswordResetForm,
    TrustForgeSetPasswordForm,
)
from .models import Business, BusinessMembership, Customer, Feedback, Job, JobMedia, ProofCard, ReviewRequest

User = get_user_model()
ACTIVE_BUSINESS_SESSION_KEY = 'trustforge_active_business_id'
POSITIVE_EXPERIENCES = {Feedback.Experience.GREAT, Feedback.Experience.GOOD}
RATING_MAP = {
    Feedback.Experience.GREAT: 5,
    Feedback.Experience.GOOD: 4,
    Feedback.Experience.OKAY: 3,
    Feedback.Experience.BAD: 2,
}


def _theme_context() -> dict:
    return {
        'project_name': 'TrustForge',
        'project_description': (
            'TrustForge turns completed service jobs into proof cards, testimonials, and conversion assets '
            'for contractors, HVAC teams, roofers, plumbers, and local service businesses.'
        ),
        'project_image_url': os.getenv('PROJECT_IMAGE_URL', ''),
    }


class ThemedAuthContextMixin:
    auth_page_title = 'TrustForge Account'
    auth_page_description = 'Secure access to your proof pipeline, review engine, and published proof assets.'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context.update(_theme_context())
        context.setdefault('auth_page_title', self.auth_page_title)
        context.setdefault('auth_page_description', self.auth_page_description)
        return context


class TrustForgeLoginView(ThemedAuthContextMixin, LoginView):
    template_name = 'registration/login.html'
    authentication_form = TrustForgeAuthenticationForm
    redirect_authenticated_user = True
    auth_page_title = 'Welcome back to your trust engine'
    auth_page_description = 'Sign in to manage completed jobs, review requests, proof cards, and every asset that helps the next customer say yes.'

    def get_success_url(self):
        redirect_url = self.get_redirect_url()
        if redirect_url:
            return redirect_url
        if _get_active_membership(self.request) is None:
            return reverse('business_onboarding')
        return reverse('dashboard')


class TrustForgePasswordResetView(ThemedAuthContextMixin, PasswordResetView):
    template_name = 'registration/password_reset_form.html'
    email_template_name = 'registration/password_reset_email.txt'
    subject_template_name = 'registration/password_reset_subject.txt'
    success_url = reverse_lazy('password_reset_done')
    form_class = TrustForgePasswordResetForm
    auth_page_title = 'Reset your TrustForge password'
    auth_page_description = 'Enter your work email and we will send a secure reset link so you can get back into your proof pipeline.'


class TrustForgePasswordResetDoneView(ThemedAuthContextMixin, PasswordResetDoneView):
    template_name = 'registration/password_reset_done.html'
    auth_page_title = 'Check your email'
    auth_page_description = 'If that email is tied to an account, a secure reset link is on its way.'


class TrustForgePasswordResetConfirmView(ThemedAuthContextMixin, PasswordResetConfirmView):
    template_name = 'registration/password_reset_confirm.html'
    form_class = TrustForgeSetPasswordForm
    success_url = reverse_lazy('password_reset_complete')
    auth_page_title = 'Create a new password'
    auth_page_description = 'Set a new password for your account and return to the TrustForge dashboard securely.'


class TrustForgePasswordResetCompleteView(ThemedAuthContextMixin, PasswordResetCompleteView):
    template_name = 'registration/password_reset_complete.html'
    auth_page_title = 'Password updated'
    auth_page_description = 'Your password has been changed successfully. You can sign back into TrustForge now.'


def _get_memberships_queryset(user):
    return BusinessMembership.objects.select_related('business').filter(user=user, business__is_active=True)


def _get_user_memberships(request: HttpRequest) -> list[BusinessMembership]:
    if not request.user.is_authenticated:
        return []
    cached = getattr(request, '_trustforge_memberships', None)
    if cached is None:
        cached = list(_get_memberships_queryset(request.user))
        request._trustforge_memberships = cached
    return cached


def _get_active_membership(request: HttpRequest) -> BusinessMembership | None:
    if not request.user.is_authenticated:
        return None
    cached = getattr(request, '_trustforge_active_membership', None)
    if cached is not None:
        return cached

    memberships = _get_user_memberships(request)
    active_business_id = request.session.get(ACTIVE_BUSINESS_SESSION_KEY)
    membership = next((item for item in memberships if item.business_id == active_business_id), None)
    if membership is None and memberships:
        membership = memberships[0]
    request._trustforge_active_membership = membership
    return membership


def _set_active_membership(request: HttpRequest, business_id: int) -> None:
    request.session[ACTIVE_BUSINESS_SESSION_KEY] = business_id
    request._trustforge_active_membership = None


def _generate_unique_business_slug(name: str) -> str:
    base_slug = slugify(name)[:45] or 'business'
    candidate = base_slug
    counter = 2
    while Business.objects.filter(slug=candidate).exists():
        candidate = f'{base_slug}-{counter}'[:55]
        counter += 1
    return candidate


def business_required(view_func):
    @wraps(view_func)
    def wrapped(request: HttpRequest, *args, **kwargs):
        if _get_active_membership(request) is None:
            messages.info(request, 'Create or join a business workspace to unlock your protected TrustForge pipeline.')
            return redirect('business_onboarding')
        return view_func(request, *args, **kwargs)

    return wrapped


def membership_role_required(*allowed_roles: str):
    def decorator(view_func):
        @wraps(view_func)
        def wrapped(request: HttpRequest, *args, **kwargs):
            membership = _get_active_membership(request)
            if membership is None:
                messages.info(request, 'Create or join a business workspace to continue.')
                return redirect('business_onboarding')
            if membership.role not in allowed_roles:
                raise PermissionDenied('Your role does not allow this action in the current workspace.')
            return view_func(request, *args, **kwargs)

        return wrapped

    return decorator


def _build_review_link(request: HttpRequest, review_request: ReviewRequest) -> str:
    return request.build_absolute_uri(reverse('review_request', args=[str(review_request.token)]))


@transaction.atomic
def _create_review_request(request: HttpRequest, job: Job, channel: str) -> ReviewRequest:
    review_request, created = ReviewRequest.objects.get_or_create(
        job=job,
        defaults={
            'channel': channel,
            'status': ReviewRequest.Status.SENT,
        },
    )
    if created:
        review_request.delivery_note = 'Share this link manually from the field app.'
    if channel == ReviewRequest.Channel.EMAIL and job.customer.email:
        review_link = _build_review_link(request, review_request)
        email_sent = send_mail(
            subject=f'How was your {job.service_type.lower()} experience?',
            message=(
                f'Hi {job.customer.full_name}\n\n'
                f'Thanks for choosing {job.business.name}. Please share your feedback here: {review_link}\n\n'
                'Your response helps us build verified proof of work for future customers.'
            ),
            from_email=None,
            recipient_list=[job.customer.email],
            fail_silently=True,
        )
        review_request.delivery_note = 'Email sent automatically.' if email_sent else 'Email backend unavailable — copy link manually.'
    review_request.status = ReviewRequest.Status.SENT
    review_request.sent_at = timezone.now()
    review_request.save()
    job.status = Job.Status.REVIEW_REQUESTED
    job.save(update_fields=['status'])
    return review_request


@transaction.atomic
def signup(request: HttpRequest) -> HttpResponse:
    if request.user.is_authenticated:
        return redirect('dashboard' if _get_active_membership(request) else 'business_onboarding')

    if request.method == 'POST':
        form = SignUpForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            messages.success(request, 'Your TrustForge account is ready. Now let’s create your first business workspace.')
            return redirect('business_onboarding')
    else:
        form = SignUpForm()

    context = {
        **_theme_context(),
        'auth_page_title': 'Create your TrustForge account',
        'auth_page_description': 'Start with secure account access, then connect your business workspace, team roles, and protected proof pipeline.',
        'form': form,
    }
    return render(request, 'registration/signup.html', context)


@login_required
@transaction.atomic
def business_onboarding(request: HttpRequest) -> HttpResponse:
    current_membership = _get_active_membership(request)
    if current_membership is not None:
        return redirect('dashboard')

    if request.method == 'POST':
        form = BusinessOnboardingForm(request.POST)
        if form.is_valid():
            business = form.save(commit=False)
            business.slug = _generate_unique_business_slug(business.name)
            business.save()
            membership = BusinessMembership.objects.create(
                business=business,
                user=request.user,
                role=BusinessMembership.Role.OWNER,
            )
            _set_active_membership(request, membership.business_id)
            messages.success(request, 'Workspace created. Your jobs, proof cards, and reviews are now scoped to this business.')
            return redirect('dashboard')
    else:
        form = BusinessOnboardingForm()

    context = {
        **_theme_context(),
        'form': form,
    }
    return render(request, 'core/business_onboarding.html', context)


@login_required
@transaction.atomic
def switch_workspace(request: HttpRequest, business_id: int) -> HttpResponse:
    membership = get_object_or_404(_get_memberships_queryset(request.user), business_id=business_id)
    _set_active_membership(request, membership.business_id)
    messages.success(request, f'Workspace switched to {membership.business.name}.')
    next_url = request.POST.get('next') or reverse('dashboard')
    return redirect(next_url)


@login_required
@transaction.atomic
def profile_settings(request: HttpRequest) -> HttpResponse:
    current_membership = _get_active_membership(request)
    memberships = _get_user_memberships(request)

    if request.method == 'POST':
        form = ProfileSettingsForm(request.POST, instance=request.user)
        if form.is_valid():
            form.save()
            messages.success(request, 'Profile settings updated.')
            return redirect('profile_settings')
    else:
        form = ProfileSettingsForm(instance=request.user)

    context = {
        **_theme_context(),
        'form': form,
        'current_membership': current_membership,
        'memberships': memberships,
    }
    return render(request, 'core/profile_settings.html', context)


@login_required
@membership_role_required(BusinessMembership.Role.OWNER, BusinessMembership.Role.ADMIN)
@transaction.atomic
def workspace_settings(request: HttpRequest) -> HttpResponse:
    current_membership = _get_active_membership(request)
    business = current_membership.business
    team_members = BusinessMembership.objects.select_related('user').filter(business=business).order_by('created_at', 'id')

    if request.method == 'POST':
        action = request.POST.get('action')
        if action == 'update_business':
            business_form = BusinessSettingsForm(request.POST, instance=business)
            invite_form = TeamMemberInviteForm()
            if business_form.is_valid():
                business_form.save()
                messages.success(request, 'Workspace settings updated.')
                return redirect('workspace_settings')
        elif action == 'invite_member':
            business_form = BusinessSettingsForm(instance=business)
            invite_form = TeamMemberInviteForm(request.POST)
            if invite_form.is_valid():
                email = invite_form.cleaned_data['email']
                user, created = User.objects.get_or_create(
                    email=email,
                    defaults={
                        'username': email,
                        'email': email,
                        'first_name': invite_form.cleaned_data.get('first_name', '').strip(),
                        'last_name': invite_form.cleaned_data.get('last_name', '').strip(),
                    },
                )
                if created:
                    user.set_unusable_password()
                    user.save(update_fields=['password'])
                else:
                    updated_fields = []
                    first_name = invite_form.cleaned_data.get('first_name', '').strip()
                    last_name = invite_form.cleaned_data.get('last_name', '').strip()
                    if first_name and not user.first_name:
                        user.first_name = first_name
                        updated_fields.append('first_name')
                    if last_name and not user.last_name:
                        user.last_name = last_name
                        updated_fields.append('last_name')
                    if updated_fields:
                        user.save(update_fields=updated_fields)

                membership, membership_created = BusinessMembership.objects.update_or_create(
                    business=business,
                    user=user,
                    defaults={'role': invite_form.cleaned_data['role']},
                )
                if membership_created or created:
                    messages.success(request, 'Team member added. If this is a brand-new user, they can use “Forgot password” to set access.')
                else:
                    messages.success(request, 'Team member role updated for this workspace.')
                return redirect('workspace_settings')
        else:
            business_form = BusinessSettingsForm(instance=business)
            invite_form = TeamMemberInviteForm()
    else:
        business_form = BusinessSettingsForm(instance=business)
        invite_form = TeamMemberInviteForm()

    context = {
        **_theme_context(),
        'business_form': business_form,
        'invite_form': invite_form,
        'current_membership': current_membership,
        'team_members': team_members,
    }
    return render(request, 'core/workspace_settings.html', context)


@transaction.atomic
def home(request: HttpRequest) -> HttpResponse:
    businesses = Business.objects.count()
    stats = Job.objects.aggregate(
        completed_jobs=Count('id'),
        review_requests=Count('review_request'),
        proof_cards=Count('proof_card'),
        published_proof=Count('proof_card', filter=Q(proof_card__status=ProofCard.Status.PUBLISHED)),
    )
    featured_proofs = ProofCard.objects.select_related('job__customer', 'job__business').prefetch_related('job__media').filter(
        is_featured=True
    )[:3]
    recent_jobs = Job.objects.select_related('customer', 'business').prefetch_related('media')[:4]

    context = {
        **_theme_context(),
        'django_version': django_version(),
        'python_version': platform.python_version(),
        'current_time': timezone.now(),
        'business_count': businesses,
        'stats': stats,
        'featured_proofs': featured_proofs,
        'recent_jobs': recent_jobs,
    }
    return render(request, 'core/index.html', context)


@login_required
@business_required
@transaction.atomic
def dashboard(request: HttpRequest) -> HttpResponse:
    current_membership = _get_active_membership(request)
    business = current_membership.business
    jobs = Job.objects.filter(business=business)
    stats = jobs.aggregate(
        completed_jobs=Count('id'),
        review_requests=Count('review_request'),
        proof_cards=Count('proof_card'),
        published_cards=Count('proof_card', filter=Q(proof_card__status=ProofCard.Status.PUBLISHED)),
    )
    feedback_qs = Feedback.objects.filter(review_request__job__business=business)
    positive_feedback = feedback_qs.filter(experience__in=POSITIVE_EXPERIENCES).count()
    total_feedback = feedback_qs.count()
    conversion_rate = round((positive_feedback / total_feedback) * 100, 1) if total_feedback else 0

    recent_jobs = jobs.select_related('customer', 'business').prefetch_related('media')[:5]
    recent_proofs = ProofCard.objects.select_related('job__customer', 'job__business').prefetch_related('job__media').filter(job__business=business)[:4]

    context = {
        **_theme_context(),
        'current_membership': current_membership,
        'stats': stats,
        'conversion_rate': conversion_rate,
        'recent_jobs': recent_jobs,
        'recent_proofs': recent_proofs,
    }
    return render(request, 'core/dashboard.html', context)


@login_required
@business_required
@transaction.atomic
def jobs_list(request: HttpRequest) -> HttpResponse:
    current_membership = _get_active_membership(request)
    jobs = Job.objects.select_related('customer', 'business').prefetch_related('media').filter(business=current_membership.business)
    context = {**_theme_context(), 'jobs': jobs, 'current_membership': current_membership}
    return render(request, 'core/jobs_list.html', context)


@login_required
@business_required
@transaction.atomic
def job_create(request: HttpRequest) -> HttpResponse:
    current_membership = _get_active_membership(request)
    business = current_membership.business

    if request.method == 'POST':
        form = JobIntakeForm(request.POST, request.FILES, business=business)
        if form.is_valid():
            customer = Customer.objects.create(
                business=business,
                full_name=form.cleaned_data['customer_name'],
                email=form.cleaned_data['customer_email'],
                phone=form.cleaned_data['customer_phone'],
                city=form.cleaned_data['customer_city'],
                state=form.cleaned_data['customer_state'],
            )
            job = Job.objects.create(
                business=business,
                customer=customer,
                service_type=form.cleaned_data['service_type'],
                description=form.cleaned_data['description'],
                technician_name=form.cleaned_data['technician_name'],
                city=form.cleaned_data['customer_city'],
                state=form.cleaned_data['customer_state'],
                completed_at=form.cleaned_data['completion_date'],
                project_value=form.cleaned_data['project_value'],
                status=Job.Status.COMPLETED,
            )
            for media_type, upload in (
                (JobMedia.MediaType.BEFORE, form.cleaned_data.get('before_photo')),
                (JobMedia.MediaType.AFTER, form.cleaned_data.get('after_photo')),
            ):
                if upload:
                    JobMedia.objects.create(job=job, media_type=media_type, file=upload)

            display_name = 'Verified homeowner' if form.cleaned_data['anonymize_customer'] else customer.full_name
            ProofCard.objects.create(
                job=job,
                customer_display_name=display_name,
                is_anonymized=form.cleaned_data['anonymize_customer'],
                attached_widget_label='Homepage proof gallery',
                attached_pages='Homepage, Service pages',
                status=ProofCard.Status.DRAFT,
            )

            if form.cleaned_data['send_review_request']:
                _create_review_request(request, job, form.cleaned_data['review_channel'])

            messages.success(request, 'Job logged inside your workspace. Proof card drafted and ready for review workflow.')
            return redirect('job_detail', job_id=job.id)
    else:
        form = JobIntakeForm(
            business=business,
            initial={
                'business': business,
                'customer_city': business.primary_city,
                'customer_state': business.primary_state,
                'technician_name': request.user.get_full_name(),
            },
        )

    context = {**_theme_context(), 'form': form, 'current_membership': current_membership}
    return render(request, 'core/job_form.html', context)


@login_required
@business_required
@transaction.atomic
def job_detail(request: HttpRequest, job_id: int) -> HttpResponse:
    current_membership = _get_active_membership(request)
    job = get_object_or_404(
        Job.objects.select_related('customer', 'business', 'proof_card', 'review_request').prefetch_related('media'),
        id=job_id,
        business=current_membership.business,
    )
    if request.method == 'POST' and request.POST.get('action') == 'send_review_request':
        channel = request.POST.get('channel', ReviewRequest.Channel.EMAIL)
        review_request = _create_review_request(request, job, channel)
        messages.success(request, f'Review request sent. Share link: {_build_review_link(request, review_request)}')
        return redirect('job_detail', job_id=job.id)

    context = {**_theme_context(), 'job': job, 'current_membership': current_membership}
    return render(request, 'core/job_detail.html', context)


@login_required
@business_required
@transaction.atomic
def proof_cards_list(request: HttpRequest) -> HttpResponse:
    current_membership = _get_active_membership(request)
    proof_cards = ProofCard.objects.select_related('job__customer', 'job__business').prefetch_related('job__media').filter(
        job__business=current_membership.business
    )
    context = {**_theme_context(), 'proof_cards': proof_cards, 'current_membership': current_membership}
    return render(request, 'core/proof_cards_list.html', context)


@login_required
@business_required
@transaction.atomic
def proof_card_detail(request: HttpRequest, card_id: int) -> HttpResponse:
    current_membership = _get_active_membership(request)
    proof_card = get_object_or_404(
        ProofCard.objects.select_related('job__customer', 'job__business').prefetch_related('job__media'),
        id=card_id,
        job__business=current_membership.business,
    )
    if request.method == 'POST':
        if not current_membership.can_manage_proof:
            raise PermissionDenied('Your role does not allow proof publishing controls.')
        action = request.POST.get('action')
        if action == 'publish':
            proof_card.status = ProofCard.Status.PUBLISHED
            proof_card.published_at = timezone.now()
            proof_card.save(update_fields=['status', 'published_at', 'updated_at'])
            proof_card.job.status = Job.Status.PROOF_READY
            proof_card.job.save(update_fields=['status'])
            messages.success(request, 'Proof card published to the trust gallery.')
        elif action == 'hide':
            proof_card.status = ProofCard.Status.HIDDEN
            proof_card.save(update_fields=['status', 'updated_at'])
            messages.success(request, 'Proof card hidden from public display.')
        elif action == 'toggle_featured':
            proof_card.is_featured = not proof_card.is_featured
            proof_card.save(update_fields=['is_featured', 'updated_at'])
            messages.success(request, 'Featured flag updated.')
        return redirect('proof_card_detail', card_id=proof_card.id)

    context = {**_theme_context(), 'proof_card': proof_card, 'current_membership': current_membership}
    return render(request, 'core/proof_card_detail.html', context)


@login_required
@membership_role_required(BusinessMembership.Role.OWNER, BusinessMembership.Role.ADMIN, BusinessMembership.Role.MANAGER)
@transaction.atomic
def proof_card_edit(request: HttpRequest, card_id: int) -> HttpResponse:
    current_membership = _get_active_membership(request)
    proof_card = get_object_or_404(
        ProofCard.objects.select_related('job__customer', 'job__business'),
        id=card_id,
        job__business=current_membership.business,
    )
    if request.method == 'POST':
        form = ProofCardForm(request.POST, instance=proof_card)
        if form.is_valid():
            proof_card = form.save(commit=False)
            if proof_card.status == ProofCard.Status.PUBLISHED and not proof_card.published_at:
                proof_card.published_at = timezone.now()
            proof_card.save()
            messages.success(request, 'Proof card updated.')
            return redirect('proof_card_detail', card_id=proof_card.id)
    else:
        form = ProofCardForm(instance=proof_card)

    context = {**_theme_context(), 'form': form, 'proof_card': proof_card, 'current_membership': current_membership}
    return render(request, 'core/proof_card_form.html', context)


@transaction.atomic
def review_request_view(request: HttpRequest, token: str) -> HttpResponse:
    review_request = get_object_or_404(
        ReviewRequest.objects.select_related('job__customer', 'job__business').prefetch_related('job__media'),
        token=token,
    )
    proof_card = review_request.job.proof_card
    if review_request.status == ReviewRequest.Status.SENT:
        review_request.status = ReviewRequest.Status.VIEWED
        review_request.last_opened_at = timezone.now()
        review_request.save(update_fields=['status', 'last_opened_at'])

    submitted = False
    positive = False
    redirect_url = review_request.job.business.google_review_url

    if request.method == 'POST':
        form = PublicFeedbackForm(request.POST)
        if form.is_valid():
            experience = form.cleaned_data['experience']
            testimonial = form.cleaned_data['testimonial'].strip()
            positive = experience in POSITIVE_EXPERIENCES
            feedback, _ = Feedback.objects.update_or_create(
                review_request=review_request,
                defaults={
                    'experience': experience,
                    'rating': RATING_MAP[experience],
                    'testimonial': testimonial,
                    'follow_up_required': not positive,
                    'is_public_approved': positive,
                },
            )
            review_request.status = ReviewRequest.Status.RESPONDED
            review_request.reviewed_at = timezone.now()
            review_request.save(update_fields=['status', 'reviewed_at'])

            proof_card.rating = feedback.rating
            proof_card.testimonial_quote = testimonial
            if positive:
                proof_card.status = ProofCard.Status.PUBLISHED
                proof_card.published_at = timezone.now()
            else:
                proof_card.status = ProofCard.Status.DRAFT
            proof_card.save(update_fields=['rating', 'testimonial_quote', 'status', 'published_at', 'updated_at'])
            review_request.job.status = Job.Status.PROOF_READY if positive else Job.Status.REVIEW_REQUESTED
            review_request.job.save(update_fields=['status'])
            submitted = True
            form = PublicFeedbackForm()
    else:
        form = PublicFeedbackForm()

    context = {
        **_theme_context(),
        'review_request': review_request,
        'job': review_request.job,
        'proof_card': proof_card,
        'form': form,
        'submitted': submitted,
        'positive': positive,
        'redirect_url': redirect_url,
    }
    return render(request, 'core/review_request.html', context)
