from django.contrib import admin
from django.contrib import messages
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.urls import path
from django.utils import timezone

from apps.courses.models import (
    AttemptEvent,
    AttemptQuestion,
    Category,
    CertificateTemplate,
    Course,
    CourseBundle,
    CourseCertificate,
    CourseExamAttempt,
    CoursePricingHistory,
    CourseQuestion,
    DiagnosticAttempt,
    LearningSubscription,
    Lesson,
    Module,
    PracticalSubmission,
    Project,
    ProjectSubmission,
    UserCourseProgress,
)
from apps.courses.models import (
    CERT_STATUS_REVOKED,
    CERT_STATUS_VALID,
    PRACTICAL_APPROVED,
    PRACTICAL_NEEDS_REVISION,
    PRACTICAL_PENDING,
    PRACTICAL_REJECTED,
    SUBMISSION_APPROVED,
    SUBMISSION_NEEDS_REVISION,
    SUBMISSION_PENDING,
    SUBMISSION_REJECTED,
)
from apps.courses.question_import import (
    clear_course_question_bank,
    csv_template_content,
    import_questions,
    parse_upload,
)
from apps.courses.certificate_service import revoke_certificate
from apps.courses.practical_service import review_practical


@admin.register(CourseBundle)
class CourseBundleAdmin(admin.ModelAdmin):
    list_display = ('name', 'discount_percent', 'is_active', 'created_at')
    list_filter = ('is_active',)
    prepopulated_fields = {'slug': ('name',)}
    search_fields = ('name', 'description')


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name', 'applies_to', 'order', 'is_active')
    list_filter = ('applies_to', 'is_active')
    prepopulated_fields = {'slug': ('name',)}
    search_fields = ('name', 'description')


@admin.register(CertificateTemplate)
class CertificateTemplateAdmin(admin.ModelAdmin):
    list_display = ('name', 'slug', 'is_active', 'created_at')
    list_filter = ('is_active',)
    prepopulated_fields = {'slug': ('name',)}
    search_fields = ('name', 'description')


class ModuleInline(admin.TabularInline):
    model = Module
    extra = 1
    ordering = ['order']
    fields = (
        'order', 'title', 'module_type', 'content_type', 'is_free_preview',
        'linked_project', 'description', 'content_url', 'pdf_file',
    )
    raw_id_fields = ('linked_project',)


class ProjectInline(admin.TabularInline):
    model = Project
    extra = 1
    ordering = ['order']
    fields = ('order', 'title', 'submission_type', 'is_active', 'brief_document_url')


class LessonInline(admin.TabularInline):
    model = Lesson
    extra = 1
    ordering = ['order']


@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
    list_display = (
        'title', 'category', 'level', 'status', 'is_active', 'is_featured',
        'currency', 'learning_price', 'exam_fee', 'discount_price',
        'bundle', 'requires_practical', 'created_at',
    )
    list_filter = (
        'status', 'is_active', 'is_featured', 'level', 'category', 'currency',
        'requires_practical', 'has_learning_content', 'mentorship_available',
        'subscription_enabled', 'bundle',
    )
    list_editable = ('is_featured',)
    search_fields = ('title', 'short_description', 'full_description', 'slug')
    prepopulated_fields = {'slug': ('title',)}
    filter_horizontal = ('teachers',)
    inlines = [ProjectInline, ModuleInline]
    fieldsets = (
        (None, {
            'fields': (
                'title', 'slug', 'category', 'thumbnail', 'badge_icon',
                'short_description', 'full_description', 'level',
                'status', 'is_active', 'is_featured', 'enable_free_diagnostic', 'teachers',
            ),
        }),
        ('Pricing & duration (editable — no code deploy needed)', {
            'fields': (
                'currency', 'duration_weeks',
                'learning_price', 'mentorship_price', 'mentorship_available',
                'exam_fee', 'discount_price', 'direct_exam_only_price', 'reattempt_fee',
                'bundle_discount_percent', 'bundle', 'price',
            ),
            'description': (
                'Per-course fees for pricing experiments. Changes are logged to '
                'Course pricing history automatically.'
            ),
        }),
        ('Monthly subscription (Learning Path)', {
            'fields': (
                'subscription_enabled', 'subscription_price_monthly',
                'min_subscription_months', 'estimated_completion_months',
            ),
            'description': (
                'Manual monthly renew (Stripe/JazzCash recurring not wired yet). '
                'Candidates renew each period; reminders can be sent later.'
            ),
        }),
        ('Exam defaults', {
            'fields': (
                'pass_percentage', 'question_count_per_attempt',
                'total_bank_size', 'duration_minutes',
                'learning_completion_required_percent',
                'certificate_template', 'certificate_code',
            ),
        }),
        ('Path flags', {
            'fields': (
                'has_learning_content', 'requires_practical',
                'practical_instructions', 'practical_rubric',
            ),
        }),
        ('Schedule labels', {
            'fields': ('duration', 'start_date', 'end_date'),
        }),
    )

    def save_model(self, request, obj, form, change):
        obj._pricing_changed_by = request.user
        super().save_model(request, obj, form, change)

    @admin.display(description='Enrollment')
    def enrollment_status(self, obj):
        return 'Open' if obj.is_enrollment_open else 'Closed'

    @admin.display(description='Theory/Practice')
    def theory_practice(self, obj):
        t, p = obj.get_theory_practice_ratio()
        return f'{t}% / {p}%'


@admin.register(Module)
class ModuleAdmin(admin.ModelAdmin):
    list_display = ('title', 'course', 'module_type', 'content_type', 'order', 'is_free_preview')
    list_filter = ('module_type', 'content_type', 'is_free_preview', 'course__category', 'course')
    search_fields = ('title', 'description', 'course__title')
    inlines = [LessonInline]
    raw_id_fields = ('linked_project',)


@admin.register(Lesson)
class LessonAdmin(admin.ModelAdmin):
    list_display = ('title', 'module', 'order', 'is_preview')
    list_filter = ('is_preview', 'module__course')
    search_fields = ('title',)


@admin.register(UserCourseProgress)
class UserCourseProgressAdmin(admin.ModelAdmin):
    list_display = ('user', 'course', 'module', 'completed_at', 'is_complete')
    list_filter = ('course', 'completed_at')
    search_fields = ('user__username', 'user__email', 'course__title', 'module__title')
    readonly_fields = ('completed_at',)

    @admin.display(boolean=True, description='Complete')
    def is_complete(self, obj):
        return obj.is_complete


@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
    list_display = ('title', 'course', 'submission_type', 'order', 'is_active', 'created_at')
    list_filter = ('submission_type', 'is_active', 'course__category', 'course')
    search_fields = ('title', 'description', 'course__title')


@admin.register(ProjectSubmission)
class ProjectSubmissionAdmin(admin.ModelAdmin):
    """Mentor/admin review queue for Learning Path projects."""
    list_display = (
        'project', 'user', 'status', 'submitted_at', 'reviewed_at', 'reviewed_by',
    )
    list_filter = ('status', 'project__course', 'submitted_at')
    search_fields = (
        'user__username', 'user__email', 'user__full_name',
        'project__title', 'project__course__title', 'mentor_feedback',
    )
    readonly_fields = ('submitted_at',)
    raw_id_fields = ('user', 'project', 'reviewed_by')
    actions = ('approve_submissions', 'mark_needs_revision', 'reject_submissions')

    @admin.action(description='Approve selected submissions')
    def approve_submissions(self, request, queryset):
        updated = queryset.update(
            status=SUBMISSION_APPROVED,
            reviewed_at=timezone.now(),
            reviewed_by=request.user,
        )
        self.message_user(request, f'Approved {updated} submission(s).')

    @admin.action(description='Mark as needs revision')
    def mark_needs_revision(self, request, queryset):
        updated = queryset.update(
            status=SUBMISSION_NEEDS_REVISION,
            reviewed_at=timezone.now(),
            reviewed_by=request.user,
        )
        self.message_user(request, f'Marked {updated} as needs revision.')

    @admin.action(description='Reject selected submissions')
    def reject_submissions(self, request, queryset):
        updated = queryset.update(
            status=SUBMISSION_REJECTED,
            reviewed_at=timezone.now(),
            reviewed_by=request.user,
        )
        self.message_user(request, f'Rejected {updated} submission(s).')


@admin.register(CoursePricingHistory)
class CoursePricingHistoryAdmin(admin.ModelAdmin):
    list_display = (
        'course', 'field_changed', 'old_price', 'new_price',
        'changed_by', 'changed_at',
    )
    list_filter = ('field_changed', 'changed_at', 'course__category')
    search_fields = ('course__title', 'field_changed', 'old_price', 'new_price')
    readonly_fields = (
        'course', 'field_changed', 'old_price', 'new_price',
        'changed_by', 'changed_at', 'note',
    )

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        return False


@admin.register(LearningSubscription)
class LearningSubscriptionAdmin(admin.ModelAdmin):
    list_display = (
        'user', 'course', 'status', 'monthly_price_locked', 'currency_locked',
        'months_paid', 'current_period_end', 'started_at', 'cancelled_at',
    )
    list_filter = ('status', 'currency_locked', 'course__category', 'course')
    search_fields = (
        'user__username', 'user__email', 'user__full_name', 'course__title',
    )
    raw_id_fields = ('user', 'course')
    readonly_fields = ('started_at',)
    actions = ('mark_cancelled', 'extend_one_month')

    @admin.action(description='Cancel selected subscriptions')
    def mark_cancelled(self, request, queryset):
        from apps.courses.models import SUB_CANCELLED
        updated = queryset.exclude(status=SUB_CANCELLED).update(
            status=SUB_CANCELLED,
            cancelled_at=timezone.now(),
        )
        self.message_user(request, f'Cancelled {updated} subscription(s).')

    @admin.action(description='Record manual renew (+1 month)')
    def extend_one_month(self, request, queryset):
        from datetime import timedelta
        from apps.courses.models import SUB_ACTIVE
        count = 0
        for sub in queryset.filter(status=SUB_ACTIVE):
            sub.current_period_end = sub.current_period_end + timedelta(days=30)
            sub.months_paid += 1
            sub.save(update_fields=['current_period_end', 'months_paid'])
            count += 1
        self.message_user(request, f'Extended {count} subscription(s) by ~1 month.')


class AttemptQuestionInline(admin.TabularInline):
    model = AttemptQuestion
    extra = 0
    can_delete = False
    fields = (
        'order_index', 'question', 'correct_display_option',
        'selected_display_option', 'presented_question_text',
    )
    readonly_fields = fields
    show_change_link = True

    def has_add_permission(self, request, obj=None):
        return False


@admin.register(CourseQuestion)
class CourseQuestionAdmin(admin.ModelAdmin):
    """Question bank + CSV/Excel import + SME verification workflow."""
    list_display = (
        'id', 'course', 'short_text', 'difficulty', 'topic_tag',
        'is_active', 'is_verified', 'created_by', 'last_reviewed_at',
    )
    list_filter = (
        'is_verified', 'is_active', 'difficulty', 'topic_tag',
        'course__category', 'course',
    )
    search_fields = ('question_text', 'topic_tag', 'option_a', 'option_b', 'course__title')
    raw_id_fields = ('course', 'created_by')
    list_editable = ('is_active',)
    actions = (
        'mark_verified', 'mark_unverified', 'activate', 'deactivate',
        'delete_selected_questions', 'clear_bank_for_selected_courses',
    )
    change_list_template = 'admin/courses/coursequestion/change_list.html'
    list_per_page = 100

    def get_urls(self):
        urls = super().get_urls()
        custom = [
            path(
                'import/',
                self.admin_site.admin_view(self.import_view),
                name='courses_coursequestion_import',
            ),
            path(
                'import/template.csv',
                self.admin_site.admin_view(self.template_csv_view),
                name='courses_coursequestion_import_template',
            ),
        ]
        return custom + urls

    @admin.display(description='Question')
    def short_text(self, obj):
        return (obj.question_text or '')[:80]

    @admin.action(description='Mark verified (eligible for exams)')
    def mark_verified(self, request, queryset):
        updated = queryset.update(is_verified=True, last_reviewed_at=timezone.now())
        self.message_user(request, f'Verified {updated} question(s).')

    @admin.action(description='Clear verification')
    def mark_unverified(self, request, queryset):
        updated = queryset.update(is_verified=False)
        self.message_user(request, f'Unverified {updated} question(s).')

    @admin.action(description='Activate')
    def activate(self, request, queryset):
        self.message_user(request, f'Activated {queryset.update(is_active=True)}.')

    @admin.action(description='Deactivate')
    def deactivate(self, request, queryset):
        self.message_user(request, f'Deactivated {queryset.update(is_active=False)}.')

    @admin.action(description='Permanently delete selected questions')
    def delete_selected_questions(self, request, queryset):
        """Hard-delete selection; skips rows already used in an exam attempt."""
        deletable = queryset.filter(attempt_appearances__isnull=True)
        blocked = queryset.filter(attempt_appearances__isnull=False).count()
        deleted_count, _ = deletable.delete()
        msg = f'Permanently deleted {deleted_count} question(s).'
        if blocked:
            msg += (
                f' {blocked} could not be deleted (already used in an attempt) — '
                f'deactivate those instead.'
            )
            queryset.filter(attempt_appearances__isnull=False).update(is_active=False)
        self.message_user(request, msg)

    @admin.action(description='Clear ENTIRE bank for course(s) of selected rows')
    def clear_bank_for_selected_courses(self, request, queryset):
        """
        Deletes unused questions for every course represented in the selection.
        Attempt-linked questions are deactivated only.
        """
        course_ids = list(queryset.values_list('course_id', flat=True).distinct())
        total_deleted = 0
        total_deactivated = 0
        for course in Course.objects.filter(pk__in=course_ids):
            result = clear_course_question_bank(course)
            total_deleted += result['deleted']
            total_deactivated += result['deactivated']
        self.message_user(
            request,
            f'Cleared {len(course_ids)} course bank(s): '
            f'deleted {total_deleted}, deactivated {total_deactivated} '
            f'(kept for exam audit).',
        )

    def template_csv_view(self, request):
        response = HttpResponse(csv_template_content(), content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="question_bank_template.csv"'
        return response

    def import_view(self, request):
        courses = Course.objects.filter(is_active=True).order_by('title')
        context = {
            **self.admin_site.each_context(request),
            'title': 'Import question bank (CSV / Excel)',
            'courses': courses,
            'opts': self.model._meta,
        }
        if request.method == 'POST':
            course_id = request.POST.get('course_id')
            upload = request.FILES.get('file')
            mark_verified = request.POST.get('mark_verified') == 'on'
            replace_bank = request.POST.get('replace_bank') == 'on'
            course = Course.objects.filter(pk=course_id).first()
            if not course or not upload:
                messages.error(request, 'Select a course and upload a CSV or .xlsx file.')
                return render(request, 'admin/courses/coursequestion/import.html', context)
            try:
                cleared = None
                if replace_bank:
                    cleared = clear_course_question_bank(course)
                rows = parse_upload(upload, upload.name)
                result = import_questions(
                    course,
                    rows,
                    created_by=request.user,
                    mark_verified=mark_verified,
                )
            except Exception as exc:
                messages.error(request, f'Import failed: {exc}')
                return render(request, 'admin/courses/coursequestion/import.html', context)

            if cleared:
                messages.warning(
                    request,
                    f'Replaced bank for “{course.title}”: '
                    f'deleted {cleared["deleted"]}, deactivated {cleared["deactivated"]} '
                    f'(questions already used in attempts cannot be hard-deleted).',
                )
            messages.success(
                request,
                f'Imported {result.created} question(s) into “{course.title}”. '
                f'Skipped {result.skipped}.',
            )
            for err in (result.errors or [])[:20]:
                messages.warning(request, err)
            if result.errors and len(result.errors) > 20:
                messages.warning(request, f'…and {len(result.errors) - 20} more row errors.')
            return redirect('admin:courses_coursequestion_changelist')

        return render(request, 'admin/courses/coursequestion/import.html', context)


class AttemptEventInline(admin.TabularInline):
    model = AttemptEvent
    extra = 0
    can_delete = False
    fields = ('event_type', 'detail', 'created_at')
    readonly_fields = fields

    def has_add_permission(self, request, obj=None):
        return False


@admin.register(CourseExamAttempt)
class CourseExamAttemptAdmin(admin.ModelAdmin):
    list_display = (
        'id', 'user', 'course', 'status', 'score_percent', 'passed',
        'is_flagged_for_review', 'tab_switch_count', 'started_at', 'submitted_at',
    )
    list_filter = ('status', 'passed', 'is_flagged_for_review', 'auto_submitted', 'course')
    search_fields = ('user__username', 'user__email', 'course__title', 'flag_reason')
    raw_id_fields = ('user', 'course')
    inlines = [AttemptQuestionInline, AttemptEventInline]
    readonly_fields = (
        'started_at', 'timer_started_at', 'submitted_at', 'score_percent',
        'correct_count', 'total_asked', 'passed', 'time_taken_seconds',
        'topic_breakdown', 'tab_switch_count',
    )


@admin.register(AttemptEvent)
class AttemptEventAdmin(admin.ModelAdmin):
    list_display = ('id', 'attempt', 'event_type', 'detail', 'created_at')
    list_filter = ('event_type', 'created_at')
    search_fields = ('attempt__user__username', 'detail')
    raw_id_fields = ('attempt',)
    readonly_fields = ('attempt', 'event_type', 'detail', 'metadata', 'created_at')


@admin.register(AttemptQuestion)
class AttemptQuestionAdmin(admin.ModelAdmin):
    list_display = (
        'attempt', 'order_index', 'question',
        'correct_display_option', 'selected_display_option', 'is_correct',
    )
    list_filter = ('attempt__course', 'is_correct')
    search_fields = ('presented_question_text', 'attempt__user__username')
    raw_id_fields = ('attempt', 'question')
    readonly_fields = (
        'attempt', 'question', 'order_index', 'option_order',
        'presented_question_text', 'presented_option_a', 'presented_option_b',
        'presented_option_c', 'presented_option_d', 'correct_display_option',
        'is_correct',
    )


@admin.register(CourseCertificate)
class CourseCertificateAdmin(admin.ModelAdmin):
    list_display = (
        'certificate_number', 'recipient_name', 'course', 'score_band',
        'assessment_type', 'status', 'assets_ready', 'issued_at',
    )
    list_filter = ('status', 'score_band', 'assessment_type', 'assets_ready', 'course')
    search_fields = (
        'certificate_number', 'recipient_name', 'user__username',
        'user__email', 'course__title',
    )
    raw_id_fields = ('user', 'attempt', 'course', 'revoked_by')
    readonly_fields = (
        'certificate_number', 'issued_at', 'score', 'score_band',
        'integrity_hash', 'assets_ready', 'pdf_file', 'badge_image',
        'recipient_name', 'privacy_initials_only', 'course_title_snapshot',
        'revoked_at',
    )
    actions = ('revoke_selected', 'regenerate_assets', 'restore_valid')

    @admin.action(description='Revoke selected certificates (fraud / integrity)')
    def revoke_selected(self, request, queryset):
        count = 0
        for cert in queryset.filter(status=CERT_STATUS_VALID):
            revoke_certificate(
                cert,
                reason='Revoked by admin',
                revoked_by=request.user,
            )
            count += 1
        self.message_user(request, f'Revoked {count} certificate(s).')

    @admin.action(description='Mark selected as valid (undo revoke)')
    def restore_valid(self, request, queryset):
        updated = queryset.filter(status=CERT_STATUS_REVOKED).update(
            status=CERT_STATUS_VALID,
            revoke_reason='',
            revoked_at=None,
            revoked_by=None,
        )
        self.message_user(request, f'Restored {updated} certificate(s) to valid.')

    @admin.action(description='Regenerate PDF + badge (async if Q running)')
    def regenerate_assets(self, request, queryset):
        from apps.courses.certificate_service import enqueue_certificate_assets
        for cert in queryset:
            enqueue_certificate_assets(cert.pk)
        self.message_user(request, f'Queued/regenerated assets for {queryset.count()} certificate(s).')


@admin.register(PracticalSubmission)
class PracticalSubmissionAdmin(admin.ModelAdmin):
    """Admin review queue for post-MCQ practicals (also available at /courses/practical/review/)."""
    list_display = (
        'id', 'user', 'course', 'submission_type', 'status',
        'submitted_at', 'reviewed_by', 'reviewed_at',
    )
    list_filter = ('status', 'submission_type', 'course')
    search_fields = (
        'user__username', 'user__email', 'user__full_name',
        'course__title', 'submission_url', 'reviewer_notes',
    )
    raw_id_fields = ('user', 'course', 'attempt', 'reviewed_by')
    readonly_fields = ('submitted_at', 'updated_at', 'reviewed_at')
    actions = ('approve_and_issue', 'mark_needs_revision', 'mark_rejected')
    change_list_template = 'admin/courses/practicalsubmission/change_list.html'

    @admin.action(description='Approve selected → issue certificates')
    def approve_and_issue(self, request, queryset):
        from django.core.exceptions import ValidationError
        ok, err = 0, 0
        for sub in queryset.exclude(status=PRACTICAL_APPROVED):
            try:
                review_practical(sub, reviewer=request.user, action='approve', notes=sub.reviewer_notes or 'Approved in admin')
                ok += 1
            except ValidationError:
                err += 1
        self.message_user(request, f'Approved {ok}; failed {err}.')

    @admin.action(description='Request resubmission (needs notes on each)')
    def mark_needs_revision(self, request, queryset):
        from django.core.exceptions import ValidationError
        ok = 0
        for sub in queryset.filter(status=PRACTICAL_PENDING):
            notes = sub.reviewer_notes or 'Please revise and resubmit per the rubric.'
            try:
                review_practical(sub, reviewer=request.user, action='needs_revision', notes=notes)
                ok += 1
            except ValidationError:
                pass
        self.message_user(request, f'Marked {ok} for resubmission.')

    @admin.action(description='Reject selected (uses existing notes or default)')
    def mark_rejected(self, request, queryset):
        from django.core.exceptions import ValidationError
        ok = 0
        for sub in queryset.filter(status=PRACTICAL_PENDING):
            notes = sub.reviewer_notes or 'Submission does not meet the practical requirements.'
            try:
                review_practical(sub, reviewer=request.user, action='reject', notes=notes)
                ok += 1
            except ValidationError:
                pass
        self.message_user(request, f'Rejected {ok}.')


@admin.register(DiagnosticAttempt)
class DiagnosticAttemptAdmin(admin.ModelAdmin):
    list_display = (
        'email', 'whatsapp', 'course', 'score', 'correct_count',
        'total_asked', 'follow_up_sent', 'taken_at',
    )
    list_filter = ('follow_up_sent', 'course', 'taken_at')
    search_fields = ('email', 'whatsapp', 'course__title')
    list_editable = ('follow_up_sent',)
    readonly_fields = (
        'course', 'email', 'whatsapp', 'score', 'correct_count',
        'total_asked', 'question_ids', 'taken_at',
    )
