"""
Management command to import doctor ratings from HIS API.

This command fetches doctor ratings from the HIS FetchDoctorRatingMAPI endpoint
and imports them into the system. It supports importing for specific months,
multiple months, or full historical data.

Usage:
    # Import previous month (default)
    python manage.py import_his_doctor_ratings

    # Import specific month
    python manage.py import_his_doctor_ratings --month 2026-01

    # Import last 6 months
    python manage.py import_his_doctor_ratings --months-back 6

    # Import last 12 months
    python manage.py import_his_doctor_ratings --months-back 12

    # Import all historical data
    python manage.py import_his_doctor_ratings --full-history

    # Dry run (show what would be imported)
    python manage.py import_his_doctor_ratings --month 2026-01 --dry-run
"""

import logging
from calendar import monthrange
from datetime import datetime, timedelta
from typing import List, Optional

from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone

from apps.integrations.services.his_client import HISClient
from apps.organizations.models import Hospital
from apps.physicians.adapter import DoctorRatingAdapter
from apps.physicians.models import (
    DoctorRatingImportJob,
    PhysicianIndividualRating,
)

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    help = "Import doctor ratings from HIS API"

    def add_arguments(self, parser):
        parser.add_argument("--month", type=str, help="Specific month to import (format: YYYY-MM, e.g., 2026-01)")
        parser.add_argument("--months-back", type=int, help="Import ratings for the last N months (e.g., 6 or 12)")
        parser.add_argument("--full-history", action="store_true", help="Import all historical ratings from HIS")
        parser.add_argument(
            "--dry-run", action="store_true", help="Show what would be imported without actually importing"
        )
        parser.add_argument("--force", action="store_true", help="Force import even if ratings already exist")

    def handle(self, *args, **options):
        self.dry_run = options["dry_run"]
        self.force = options["force"]

        # Determine date ranges to import
        date_ranges = self._get_date_ranges(options)

        if not date_ranges:
            raise CommandError("No date range specified. Use --month, --months-back, or --full-history")

        # Initialize HIS client
        client = HISClient()

        # Track overall stats
        total_stats = {
            "total_months": len(date_ranges),
            "total_ratings": 0,
            "success": 0,
            "failed": 0,
            "duplicates": 0,
            "staff_matched": 0,
        }

        for from_date, to_date in date_ranges:
            month_label = from_date.strftime("%Y-%m")
            self.stdout.write(f"\n{'=' * 60}")
            self.stdout.write(f"Processing month: {month_label}")
            self.stdout.write(f"Date range: {from_date} to {to_date}")
            self.stdout.write(f"{'=' * 60}\n")

            # Fetch ratings from HIS
            self.stdout.write("Fetching ratings from HIS...")
            his_data = client.fetch_doctor_ratings(from_date, to_date)

            if not his_data:
                self.stdout.write(self.style.ERROR("Failed to fetch data from HIS"))
                continue

            if his_data.get("Code") != 200:
                error_msg = his_data.get("Message", "Unknown error")
                self.stdout.write(self.style.ERROR(f"HIS API error: {error_msg}"))
                continue

            ratings_list = his_data.get("FetchDoctorRatingMAPI1List", [])

            if not ratings_list:
                self.stdout.write(self.style.WARNING("No ratings found for this period"))
                continue

            self.stdout.write(f"Found {len(ratings_list)} ratings to process\n")

            # Process ratings
            month_stats = self._process_ratings(ratings_list, from_date, to_date)

            # Update totals
            total_stats["total_ratings"] += month_stats["total"]
            total_stats["success"] += month_stats["success"]
            total_stats["failed"] += month_stats["failed"]
            total_stats["duplicates"] += month_stats["duplicates"]
            total_stats["staff_matched"] += month_stats["staff_matched"]

            # Print month summary
            self._print_month_summary(month_label, month_stats)

        # Print final summary
        self._print_final_summary(total_stats)

    def _get_date_ranges(self, options) -> List[tuple]:
        """Determine date ranges to import based on options."""
        ranges = []

        now = timezone.now()

        if options["month"]:
            # Specific month
            try:
                year, month = map(int, options["month"].split("-"))
                from_date = datetime(year, month, 1)
                last_day = monthrange(year, month)[1]
                to_date = datetime(year, month, last_day, 23, 59, 59)
                ranges.append((from_date, to_date))
            except ValueError:
                raise CommandError("Invalid month format. Use YYYY-MM (e.g., 2026-01)")

        elif options["months_back"]:
            # Last N months
            months_back = options["months_back"]
            for i in range(months_back):
                # Calculate month
                target_month = now.month - i
                target_year = now.year

                while target_month <= 0:
                    target_month += 12
                    target_year -= 1

                from_date = datetime(target_year, target_month, 1)
                last_day = monthrange(target_year, target_month)[1]
                to_date = datetime(target_year, target_month, last_day, 23, 59, 59)
                ranges.append((from_date, to_date))

        elif options["full_history"]:
            # Full history - go back 5 years
            self.stdout.write(
                self.style.WARNING("Full history import requested. This will import data for the last 5 years.")
            )
            for i in range(60):  # 5 years = 60 months
                target_month = now.month - i
                target_year = now.year

                while target_month <= 0:
                    target_month += 12
                    target_year -= 1

                from_date = datetime(target_year, target_month, 1)
                last_day = monthrange(target_year, target_month)[1]
                to_date = datetime(target_year, target_month, last_day, 23, 59, 59)
                ranges.append((from_date, to_date))

        else:
            # Default: previous month
            if now.month == 1:
                prev_month = 12
                prev_year = now.year - 1
            else:
                prev_month = now.month - 1
                prev_year = now.year

            from_date = datetime(prev_year, prev_month, 1)
            last_day = monthrange(prev_year, prev_month)[1]
            to_date = datetime(prev_year, prev_month, last_day, 23, 59, 59)
            ranges.append((from_date, to_date))

        return ranges

    def _process_ratings(self, ratings_list: List[dict], from_date: datetime, to_date: datetime) -> dict:
        """Process list of ratings from HIS."""
        stats = {
            "total": len(ratings_list),
            "success": 0,
            "failed": 0,
            "duplicates": 0,
            "staff_matched": 0,
            "errors": [],
        }

        # Create import job
        if not self.dry_run:
            # Find first hospital to create job
            first_hospital = Hospital.objects.first()
            if first_hospital:
                job = DoctorRatingImportJob.objects.create(
                    name=f"HIS Import {from_date.strftime('%Y-%m')}",
                    status=DoctorRatingImportJob.JobStatus.PROCESSING,
                    source=DoctorRatingImportJob.JobSource.HIS_API,
                    hospital=first_hospital,
                    total_records=len(ratings_list),
                    started_at=timezone.now(),
                )
            else:
                job = None
        else:
            job = None

        for idx, rating_data in enumerate(ratings_list, 1):
            # Progress indicator
            if idx % 100 == 0 or idx == len(ratings_list):
                self.stdout.write(f"  Processed {idx}/{len(ratings_list)}...")

            try:
                # Find hospital by name
                hospital_name = rating_data.get("HospitalName", "")
                hospital = Hospital.objects.filter(name__iexact=hospital_name).first()

                if not hospital:
                    # Try partial match
                    hospital = Hospital.objects.filter(name__icontains=hospital_name).first()

                if not hospital:
                    stats["failed"] += 1
                    stats["errors"].append(
                        {"row": idx, "error": f"Hospital not found: {hospital_name}", "data": rating_data}
                    )
                    continue

                if self.dry_run:
                    # Check if would be duplicate
                    doctor_id = rating_data.get("DoctorID", "")
                    rating_date_str = rating_data.get("RatingDate", "")
                    rating_date = DoctorRatingAdapter.parse_date(rating_date_str)

                    if doctor_id and rating_date:
                        existing = PhysicianIndividualRating.objects.filter(
                            doctor_id=doctor_id, rating_date=rating_date, hospital=hospital
                        ).exists()

                        if existing:
                            stats["duplicates"] += 1
                        else:
                            stats["success"] += 1
                    else:
                        stats["failed"] += 1
                else:
                    # Process the rating
                    result = DoctorRatingAdapter.process_his_rating_record(rating_data, hospital)

                    if result["is_duplicate"]:
                        stats["duplicates"] += 1
                    elif result["success"]:
                        stats["success"] += 1
                        if result["staff_matched"]:
                            stats["staff_matched"] += 1
                    else:
                        stats["failed"] += 1
                        stats["errors"].append(
                            {"row": idx, "error": result.get("message", "Unknown error"), "data": rating_data}
                        )

            except Exception as e:
                stats["failed"] += 1
                stats["errors"].append({"row": idx, "error": str(e), "data": rating_data})
                logger.error(f"Error processing rating {idx}: {e}", exc_info=True)

        # Update job status
        if job:
            job.processed_count = stats["total"]
            job.success_count = stats["success"]
            job.failed_count = stats["failed"]
            job.completed_at = timezone.now()

            if stats["failed"] == 0:
                job.status = DoctorRatingImportJob.JobStatus.COMPLETED
            elif stats["success"] == 0:
                job.status = DoctorRatingImportJob.JobStatus.FAILED
            else:
                job.status = DoctorRatingImportJob.JobStatus.PARTIAL

            job.results = {
                "stats": stats,
                "errors": stats["errors"][:50],  # Limit errors stored
            }
            job.save()

        return stats

    def _print_month_summary(self, month_label: str, stats: dict):
        """Print summary for a month."""
        self.stdout.write(f"\n{'-' * 60}")
        self.stdout.write(f"Summary for {month_label}:")
        self.stdout.write(f"  Total ratings: {stats['total']}")
        self.stdout.write(self.style.SUCCESS(f"  Successful: {stats['success']}"))
        self.stdout.write(self.style.WARNING(f"  Duplicates skipped: {stats['duplicates']}"))
        self.stdout.write(self.style.ERROR(f"  Failed: {stats['failed']}"))
        if stats["success"] > 0:
            match_pct = (stats["staff_matched"] / stats["success"]) * 100
            self.stdout.write(f"  Staff matched: {stats['staff_matched']} ({match_pct:.1f}%)")
        self.stdout.write(f"{'-' * 60}\n")

    def _print_final_summary(self, stats: dict):
        """Print final summary."""
        self.stdout.write(f"\n{'=' * 60}")
        self.stdout.write("FINAL SUMMARY")
        self.stdout.write(f"{'=' * 60}")
        self.stdout.write(f"Total months processed: {stats['total_months']}")
        self.stdout.write(f"Total ratings processed: {stats['total_ratings']}")
        self.stdout.write(self.style.SUCCESS(f"Total successful: {stats['success']}"))
        self.stdout.write(self.style.WARNING(f"Total duplicates: {stats['duplicates']}"))
        self.stdout.write(self.style.ERROR(f"Total failed: {stats['failed']}"))
        if stats["success"] > 0:
            match_pct = (stats["staff_matched"] / stats["success"]) * 100
            self.stdout.write(f"Total staff matched: {stats['staff_matched']} ({match_pct:.1f}%)")
        self.stdout.write(f"{'=' * 60}\n")
