"""
Physician Rating Import Views

UI views for manual CSV upload of doctor ratings.
Similar to HIS Patient Import flow.
"""

import csv
import io
import logging

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db import transaction
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_http_methods, require_POST

from apps.core.services import AuditService
from apps.organizations.models import Hospital

from .adapter import DoctorRatingAdapter
from .forms import DoctorRatingFetchForm, DoctorRatingImportForm
from .models import DoctorRatingImportJob, PhysicianIndividualRating
from .tasks import _fetch_and_process_his_doctor_ratings, process_doctor_rating_job

logger = logging.getLogger(__name__)


@login_required
def doctor_rating_import(request):
    """
    Import doctor ratings from CSV (Doctor Rating Report format).

    CSV Format (Doctor Rating Report):
    - Header rows (rows 1-6 contain metadata)
    - Column headers in row 7: UHID, Patient Name, Gender, Full Age, Nationality,
      Mobile No, Patient Type, Admit Date, Discharge Date, Doctor Name, Rating,
      Feed Back, Rating Date
    - Department headers appear as rows with only first column filled
    - Data rows follow
    """
    user = request.user

    # Check permission
    if not user.is_px_admin() and not user.is_hospital_admin():
        messages.error(request, "You don't have permission to import doctor ratings.")
        return redirect("physicians:physician_list")

    # Session storage for imported ratings
    session_key = f"doctor_rating_import_{user.id}"

    if request.method == "POST":
        form = DoctorRatingImportForm(request.POST, request.FILES, request=request)

        if form.is_valid():
            try:
                hospital = form.cleaned_data["hospital"]
                csv_file = form.cleaned_data["csv_file"]
                skip_rows = form.cleaned_data["skip_header_rows"]

                # Parse CSV
                decoded_file = csv_file.read().decode("utf-8-sig")
                io_string = io.StringIO(decoded_file)
                reader = csv.reader(io_string)

                # Skip header/metadata rows
                for _ in range(skip_rows):
                    next(reader, None)

                # Read header row
                header = next(reader, None)
                if not header:
                    messages.error(request, "CSV file is empty or has no data rows.")
                    return render(request, "physicians/doctor_rating_import.html", {"form": form})

                # Find column indices (handle different possible column names)
                header = [h.strip().lower() for h in header]
                col_map = {
                    "uhid": _find_column(header, ["uhid", "file number", "file_number", "mrn", "patient id"]),
                    "patient_name": _find_column(header, ["patient name", "patient_name", "name"]),
                    "gender": _find_column(header, ["gender", "sex"]),
                    "age": _find_column(header, ["full age", "age", "years"]),
                    "nationality": _find_column(header, ["nationality", "country"]),
                    "mobile_no": _find_column(header, ["mobile no", "mobile_no", "mobile", "phone", "contact"]),
                    "patient_type": _find_column(header, ["patient type", "patient_type", "type", "visit type"]),
                    "admit_date": _find_column(header, ["admit date", "admit_date", "admission date", "visit date"]),
                    "discharge_date": _find_column(header, ["discharge date", "discharge_date"]),
                    "doctor_name": _find_column(header, ["doctor name", "doctor_name", "physician name", "physician"]),
                    "rating": _find_column(header, ["rating", "score", "rate"]),
                    "feedback": _find_column(header, ["feed back", "feedback", "comments", "comment"]),
                    "rating_date": _find_column(header, ["rating date", "rating_date", "date"]),
                    "department": _find_column(header, ["department", "dept", "specialty"]),
                }

                # Check required columns
                if col_map["uhid"] is None:
                    messages.error(request, "Could not find 'UHID' column in CSV.")
                    return render(request, "physicians/doctor_rating_import.html", {"form": form})

                if col_map["doctor_name"] is None:
                    messages.error(request, "Could not find 'Doctor Name' column in CSV.")
                    return render(request, "physicians/doctor_rating_import.html", {"form": form})

                if col_map["rating"] is None:
                    messages.error(request, "Could not find 'Rating' column in CSV.")
                    return render(request, "physicians/doctor_rating_import.html", {"form": form})

                # Process data rows
                imported_ratings = []
                errors = []
                row_num = skip_rows + 1
                current_department = ""

                for row in reader:
                    row_num += 1
                    if not row or not any(row):  # Skip empty rows
                        continue

                    try:
                        # Check if this is a department header row (only first column has value)
                        if _is_department_header(row, col_map):
                            current_department = row[0].strip()
                            continue

                        # Extract data
                        uhid = _get_cell(row, col_map["uhid"], "").strip()
                        patient_name = _get_cell(row, col_map["patient_name"], "").strip()
                        doctor_name_raw = _get_cell(row, col_map["doctor_name"], "").strip()
                        rating_str = _get_cell(row, col_map["rating"], "").strip()

                        # Skip if missing required fields
                        if not uhid or not doctor_name_raw:
                            continue

                        # Validate rating
                        try:
                            rating = int(float(rating_str))
                            if rating < 1 or rating > 5:
                                errors.append(f"Row {row_num}: Invalid rating {rating}")
                                continue
                        except (ValueError, TypeError):
                            errors.append(f"Row {row_num}: Invalid rating format '{rating_str}'")
                            continue

                        # Extract optional fields
                        gender = _get_cell(row, col_map["gender"], "").strip()
                        age = _get_cell(row, col_map["age"], "").strip()
                        nationality = _get_cell(row, col_map["nationality"], "").strip()
                        mobile_no = _get_cell(row, col_map["mobile_no"], "").strip()
                        patient_type = _get_cell(row, col_map["patient_type"], "").strip()
                        admit_date = _get_cell(row, col_map["admit_date"], "").strip()
                        discharge_date = _get_cell(row, col_map["discharge_date"], "").strip()
                        feedback = _get_cell(row, col_map["feedback"], "").strip()
                        rating_date = _get_cell(row, col_map["rating_date"], "").strip()
                        department = _get_cell(row, col_map["department"], "").strip() or current_department

                        # Parse doctor name to extract ID
                        doctor_id, doctor_name_clean = DoctorRatingAdapter.parse_doctor_name(doctor_name_raw)

                        imported_ratings.append(
                            {
                                "row_num": row_num,
                                "uhid": uhid,
                                "patient_name": patient_name,
                                "doctor_name_raw": doctor_name_raw,
                                "doctor_id": doctor_id,
                                "doctor_name": doctor_name_clean,
                                "rating": rating,
                                "gender": gender,
                                "age": age,
                                "nationality": nationality,
                                "mobile_no": mobile_no,
                                "patient_type": patient_type,
                                "admit_date": admit_date,
                                "discharge_date": discharge_date,
                                "feedback": feedback,
                                "rating_date": rating_date,
                                "department": department,
                            }
                        )

                    except Exception as e:
                        errors.append(f"Row {row_num}: {str(e)}")
                        logger.error(f"Error processing row {row_num}: {e}")

                # Store in session for review step
                request.session[session_key] = {
                    "hospital_id": str(hospital.id),
                    "ratings": imported_ratings,
                    "errors": errors,
                    "total_count": len(imported_ratings),
                }

                # Log audit
                AuditService.log_event(
                    event_type="doctor_rating_csv_import",
                    description=f"Parsed {len(imported_ratings)} doctor ratings from CSV by {user.get_full_name()}",
                    user=user,
                    metadata={
                        "hospital": hospital.name,
                        "total_count": len(imported_ratings),
                        "error_count": len(errors),
                    },
                )

                if imported_ratings:
                    messages.success(
                        request,
                        f"Successfully parsed {len(imported_ratings)} doctor rating records. Please review before importing.",
                    )
                    return redirect("physicians:doctor_rating_review")
                else:
                    messages.error(request, "No valid doctor rating records found in CSV.")

            except Exception as e:
                logger.error(f"Error processing Doctor Rating CSV: {str(e)}", exc_info=True)
                messages.error(request, f"Error processing CSV: {str(e)}")
    else:
        form = DoctorRatingImportForm(request=request)

    context = {
        "form": form,
    }
    return render(request, "physicians/doctor_rating_import.html", context)


@login_required
def doctor_rating_fetch(request):
    """
    Fetch doctor ratings from HIS API by date range.

    Runs the fetch synchronously for immediate feedback, then redirects
    to the job status page (which will already show completed results).
    """
    user = request.user

    if not user.is_px_admin() and not user.is_hospital_admin():
        messages.error(request, "You don't have permission to fetch doctor ratings.")
        return redirect("physicians:physician_list")

    if request.method == "POST":
        form = DoctorRatingFetchForm(request.POST, request=request)

        if form.is_valid():
            try:
                hospital = form.cleaned_data["hospital"]
                from_date = form.cleaned_data["from_date"]
                to_date = form.cleaned_data["to_date"]

                date_label = f"{from_date.isoformat()} to {to_date.isoformat()}"

                job = DoctorRatingImportJob.objects.create(
                    name=f"HIS Fetch - {date_label}",
                    status=DoctorRatingImportJob.JobStatus.PENDING,
                    source=DoctorRatingImportJob.JobSource.HIS_API,
                    created_by=user,
                    hospital=hospital,
                )

                AuditService.log_event(
                    event_type="doctor_rating_his_fetch",
                    description=f"Fetching HIS ratings for {date_label}",
                    user=user,
                    metadata={
                        "job_id": str(job.id),
                        "hospital": hospital.name,
                        "from_date": from_date.isoformat(),
                        "to_date": to_date.isoformat(),
                    },
                )

                result = _fetch_and_process_his_doctor_ratings(str(job.id), from_date.isoformat(), to_date.isoformat())

                if result.get("success"):
                    total = result.get("total_ratings", 0)
                    if total == 0:
                        messages.info(request, f"No ratings found for {date_label}.")
                    else:
                        messages.success(
                            request,
                            f"Fetched {total} ratings from HIS: "
                            f"{result.get('success_count', 0)} imported, "
                            f"{result.get('duplicate_count', 0)} duplicates, "
                            f"{result.get('failed_count', 0)} failed.",
                        )
                else:
                    messages.error(request, f"HIS fetch failed: {result.get('error', 'Unknown error')}")

                return redirect("physicians:doctor_rating_job_status", job_id=job.id)

            except Exception as e:
                logger.error(f"Error fetching HIS ratings: {str(e)}", exc_info=True)
                messages.error(request, f"Error: {str(e)}")
    else:
        form = DoctorRatingFetchForm(request=request)

    context = {
        "form": form,
    }
    return render(request, "physicians/doctor_rating_fetch.html", context)


@login_required
def doctor_rating_review(request):
    """
    Review imported doctor ratings before creating records.
    """
    user = request.user
    session_key = f"doctor_rating_import_{user.id}"
    import_data = request.session.get(session_key)

    if not import_data:
        messages.error(request, "No import data found. Please upload CSV first.")
        return redirect("physicians:doctor_rating_import")

    hospital = get_object_or_404(Hospital, id=import_data["hospital_id"])
    ratings = import_data["ratings"]
    errors = import_data.get("errors", [])

    # Check for staff matches
    for r in ratings:
        staff = DoctorRatingAdapter.find_staff_by_doctor_id(r["doctor_id"], hospital, r["doctor_name"])
        r["staff_matched"] = staff is not None
        r["staff_name"] = staff.get_full_name() if staff else None

    if request.method == "POST":
        action = request.POST.get("action")

        if action == "import":
            # Queue bulk import job
            job = DoctorRatingImportJob.objects.create(
                name=f"CSV Import - {hospital.name} - {len(ratings)} ratings",
                status=DoctorRatingImportJob.JobStatus.PENDING,
                source=DoctorRatingImportJob.JobSource.CSV_UPLOAD,
                created_by=user,
                hospital=hospital,
                total_records=len(ratings),
                raw_data=ratings,
            )

            # Queue the background task
            process_doctor_rating_job.delay(str(job.id))

            # Log audit
            AuditService.log_event(
                event_type="doctor_rating_import_queued",
                description=f"Queued {len(ratings)} doctor ratings for import",
                user=user,
                metadata={"job_id": str(job.id), "hospital": hospital.name, "total_records": len(ratings)},
            )

            # Clear session
            del request.session[session_key]

            messages.success(request, f"Import job queued for {len(ratings)} ratings. You can check the status below.")
            return redirect("physicians:doctor_rating_job_status", job_id=job.id)

        elif action == "cancel":
            del request.session[session_key]
            messages.info(request, "Import cancelled.")
            return redirect("physicians:doctor_rating_import")

    # Pagination
    paginator = Paginator(ratings, 50)
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)

    context = {
        "hospital": hospital,
        "ratings": ratings,
        "page_obj": page_obj,
        "errors": errors,
        "total_count": len(ratings),
        "matched_count": sum(1 for r in ratings if r["staff_matched"]),
        "unmatched_count": sum(1 for r in ratings if not r["staff_matched"]),
    }
    return render(request, "physicians/doctor_rating_review.html", context)


@login_required
def doctor_rating_job_status(request, job_id):
    """
    View status of a doctor rating import job.
    """
    user = request.user
    job = get_object_or_404(DoctorRatingImportJob, id=job_id)

    # Check permission
    if not user.is_px_admin() and job.hospital != user.hospital:
        messages.error(request, "You don't have permission to view this job.")
        return redirect("physicians:physician_list")

    # Calculate progress circle stroke-dashoffset
    # Circle circumference is 326.73 (2 * pi * r, where r=52)
    # When progress is 0%, offset should be 326.73 (empty)
    # When progress is 100%, offset should be 0 (full)
    circumference = 2 * 3.14159 * 52  # ~326.73
    progress = job.progress_percentage
    stroke_dashoffset = circumference * (1 - progress / 100)

    context = {
        "job": job,
        "progress": progress,
        "stroke_dashoffset": stroke_dashoffset,
        "is_complete": job.is_complete,
        "results": job.results,
    }
    return render(request, "physicians/doctor_rating_job_status.html", context)


@login_required
def doctor_rating_job_list(request):
    """
    List all doctor rating import jobs for the user.
    """
    user = request.user

    # Filter jobs
    if user.is_px_admin():
        jobs = DoctorRatingImportJob.objects.all()
    elif user.hospital:
        jobs = DoctorRatingImportJob.objects.filter(hospital=user.hospital)
    else:
        jobs = DoctorRatingImportJob.objects.filter(created_by=user)

    jobs = jobs.order_by("-created_at")[:50]  # Last 50 jobs

    context = {
        "jobs": jobs,
    }
    return render(request, "physicians/doctor_rating_job_list.html", context)


@login_required
def individual_ratings_list(request):
    """
    List individual doctor ratings with filtering.
    """
    user = request.user

    # Base queryset
    queryset = PhysicianIndividualRating.objects.select_related("hospital", "staff", "staff__department")

    # Apply RBAC
    if not user.is_px_admin():
        if user.hospital:
            queryset = queryset.filter(hospital=user.hospital)
        else:
            queryset = queryset.none()

    # Filters
    hospital_id = request.GET.get("hospital")
    doctor_id = request.GET.get("doctor_id")
    rating_min = request.GET.get("rating_min")
    rating_max = request.GET.get("rating_max")
    date_from = request.GET.get("date_from")
    date_to = request.GET.get("date_to")
    source = request.GET.get("source")

    if hospital_id:
        queryset = queryset.filter(hospital_id=hospital_id)
    if doctor_id:
        queryset = queryset.filter(doctor_id=doctor_id)
    if rating_min:
        queryset = queryset.filter(rating__gte=int(rating_min))
    if rating_max:
        queryset = queryset.filter(rating__lte=int(rating_max))
    if date_from:
        queryset = queryset.filter(rating_date__date__gte=date_from)
    if date_to:
        queryset = queryset.filter(rating_date__date__lte=date_to)
    if source:
        queryset = queryset.filter(source=source)

    # Ordering
    queryset = queryset.order_by("-rating_date")

    # Pagination
    paginator = Paginator(queryset, 25)
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)

    # Get hospitals for filter
    from apps.organizations.models import Hospital

    if user.is_px_admin():
        hospitals = Hospital.objects.filter(status="active")
    else:
        hospitals = Hospital.objects.filter(id=user.hospital.id) if user.hospital else Hospital.objects.none()

    context = {
        "page_obj": page_obj,
        "hospitals": hospitals,
        "sources": PhysicianIndividualRating.RatingSource.choices,
        "filters": {
            "hospital": hospital_id,
            "doctor_id": doctor_id,
            "rating_min": rating_min,
            "rating_max": rating_max,
            "date_from": date_from,
            "date_to": date_to,
            "source": source,
        },
    }
    return render(request, "physicians/individual_ratings_list.html", context)


# ============================================================================
# Helper Functions
# ============================================================================


def _find_column(header, possible_names):
    """Find column index by possible names."""
    for name in possible_names:
        for i, h in enumerate(header):
            if name.lower() in h.lower():
                return i
    return None


def _get_cell(row, index, default=""):
    """Safely get cell value."""
    if index is None or index >= len(row):
        return default
    return row[index].strip() if row[index] else default


def _is_department_header(row, col_map):
    """
    Check if a row is a department header row.

    Department headers typically have:
    - First column has text (department name)
    - All other columns are empty
    """
    if not row or not row[0]:
        return False

    # Check if first column has text
    first_col = row[0].strip()
    if not first_col:
        return False

    # Check if other important columns are empty
    # If UHID, Doctor Name, Rating are all empty, it's likely a header
    uhid = _get_cell(row, col_map.get("uhid"), "").strip()
    doctor_name = _get_cell(row, col_map.get("doctor_name"), "").strip()
    rating = _get_cell(row, col_map.get("rating"), "").strip()

    # If these key fields are empty but first column has text, it's a department header
    if not uhid and not doctor_name and not rating:
        return True

    return False


# ============================================================================
# AJAX Endpoints
# ============================================================================


@login_required
def api_job_progress(request, job_id):
    """AJAX endpoint to get job progress."""
    user = request.user
    job = get_object_or_404(DoctorRatingImportJob, id=job_id)

    # Check permission
    if not user.is_px_admin() and job.hospital != user.hospital:
        return JsonResponse({"error": "Permission denied"}, status=403)

    return JsonResponse(
        {
            "job_id": str(job.id),
            "status": job.status,
            "progress_percentage": job.progress_percentage,
            "processed_count": job.processed_count,
            "total_records": job.total_records,
            "success_count": job.success_count,
            "failed_count": job.failed_count,
            "is_complete": job.is_complete,
        }
    )


@login_required
def api_match_doctor(request):
    """
    AJAX endpoint to manually match a doctor to a staff record.

    POST data:
    - doctor_id: The doctor ID from the rating
    - doctor_name: The doctor name
    - staff_id: The staff ID to match to
    """
    if request.method != "POST":
        return JsonResponse({"error": "POST required"}, status=405)

    user = request.user
    doctor_id = request.POST.get("doctor_id")
    doctor_name = request.POST.get("doctor_name")
    staff_id = request.POST.get("staff_id")

    if not staff_id:
        return JsonResponse({"error": "staff_id required"}, status=400)

    try:
        staff = Staff.objects.get(id=staff_id)

        # Check permission
        if not user.is_px_admin() and staff.hospital != user.hospital:
            return JsonResponse({"error": "Permission denied"}, status=403)

        # Update all unaggregated ratings for this doctor
        count = PhysicianIndividualRating.objects.filter(doctor_id=doctor_id, is_aggregated=False).update(staff=staff)

        return JsonResponse({"success": True, "matched_count": count, "staff_name": staff.get_full_name()})

    except Staff.DoesNotExist:
        return JsonResponse({"error": "Staff not found"}, status=404)
    except Exception as e:
        logger.error(f"Error matching doctor: {str(e)}", exc_info=True)
        return JsonResponse({"error": str(e)}, status=500)
