from django.db import models
from django.conf import settings
from django.utils import timezone
import secrets
import time


def uuid7() -> str:
    """Generate a UUID v7 (time-ordered UUID)."""
    nanoseconds = time.time_ns()
    uuid_int = (nanoseconds << 16) | secrets.randbits(48)
    return f"{uuid_int:032x}"


class Employee(models.Model):
    """Employee records linked to user accounts."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="employee")
    employee_id = models.CharField(max_length=50, unique=True)
    employment_type = models.CharField(max_length=20, choices=[
        ("staff", "Staff"),
        ("fellow", "Fellow"),
        ("intern", "Intern"),
    ], default="staff", db_index=True)
    position = models.CharField(max_length=100)
    department = models.CharField(max_length=100)
    category = models.CharField(max_length=20, blank=True, help_text="Grade/rank code, e.g. III-C")
    phone_extension = models.CharField(max_length=10, blank=True, help_text="Internal extension, e.g. 530")
    status = models.CharField(max_length=20, choices=[
        ("active", "Active"),
        ("inactive", "Inactive"),
        ("on_leave", "On Leave"),
        ("terminated", "Terminated"),
    ], default="active")
    hire_date = models.DateField()
    termination_date = models.DateField(null=True, blank=True)
    salary = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    emergency_contact_name = models.CharField(max_length=100, blank=True)
    emergency_contact_phone = models.CharField(max_length=20, blank=True)
    address = models.TextField(blank=True)
    notes = models.TextField(blank=True)
    # Per-employee overrides for org payroll/work policy. Empty {} = inherit org defaults.
    # Shape: {"transport_mode": "fixed"|"daily", "transport_amount": 0,
    #         "remote_allowed": bool, "overtime_allowed": bool,
    #         "allowance_overrides": {"<key>": amount}, ...}
    payroll_overrides = models.JSONField(default=dict, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_employees"
        ordering = ["employee_id"]

    def __str__(self):
        return f"{self.user.get_full_name()} ({self.employee_id})"


class Attendance(models.Model):
    """Employee attendance records."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name="attendance_records")
    type = models.CharField(max_length=20, choices=[
        ("check_in", "Check In"),
        ("check_out", "Check Out"),
        ("absent", "Absent"),
        ("leave", "Leave"),
    ])
    date = models.DateField()
    time = models.TimeField(null=True, blank=True)
    location = models.CharField(max_length=100, blank=True)
    photo = models.ImageField(upload_to="attendance/%Y/%m/", null=True, blank=True)
    face_match_score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "hr_attendance"
        ordering = ["-date", "-time"]
        unique_together = ["employee", "date", "type"]

    def __str__(self):
        return f"{self.employee} - {self.type} - {self.date}"


class Payroll(models.Model):
    """Employee payroll records."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name="payrolls")
    period_start = models.DateField()
    period_end = models.DateField()
    base_salary = models.DecimalField(max_digits=12, decimal_places=2)
    allowances = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    deductions = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    net_salary = models.DecimalField(max_digits=12, decimal_places=2)
    # Per-run line-item breakdown: {"earnings": [{label, amount}], "deductions": [{label, amount}], "meta": {...}}
    breakdown = models.JSONField(default=dict, blank=True)
    status = models.CharField(max_length=20, choices=[
        ("draft", "Draft"),
        ("pending", "Pending"),
        ("approved", "Approved"),
        ("paid", "Paid"),
    ], default="draft")
    approved_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True)
    paid_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "hr_payrolls"
        ordering = ["-period_start"]
        indexes = [
            # Cash statements filter paid payrolls on every request.
            models.Index(fields=["status"]),
        ]

    def __str__(self):
        return f"{self.employee} - {self.period_start} to {self.period_end}"


class Recruitment(models.Model):
    """Job recruitment/applications."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    position = models.CharField(max_length=200)
    department = models.CharField(max_length=100)
    employment_type = models.CharField(max_length=20, choices=[
        ("full_time", "Full Time"),
        ("part_time", "Part Time"),
        ("contract", "Contract"),
        ("internship", "Internship"),
    ], default="full_time")
    location = models.CharField(max_length=150, blank=True)
    salary_range = models.CharField(max_length=100, blank=True)
    deadline = models.DateField(null=True, blank=True, help_text="Application closing date")
    description = models.TextField()
    requirements = models.TextField()
    status = models.CharField(max_length=20, choices=[
        ("open", "Open"),
        ("closed", "Closed"),
        ("on_hold", "On Hold"),
    ], default="open")
    hiring_manager = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="recruitments")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_recruitments"
        ordering = ["-created_at"]


class JobApplication(models.Model):
    """A candidate's application to a Recruitment posting (public submission)."""
    STAGE_CHOICES = [
        ("applied", "Applied"),
        ("screening", "Screening"),
        ("interview", "Interview"),
        ("offer", "Offer"),
        ("hired", "Hired"),
        ("rejected", "Rejected"),
    ]
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    recruitment = models.ForeignKey(Recruitment, on_delete=models.CASCADE, related_name="applications")
    full_name = models.CharField(max_length=150)
    email = models.EmailField()
    phone = models.CharField(max_length=30, blank=True)
    resume = models.FileField(upload_to="applications/%Y/%m/", null=True, blank=True)
    writing_sample = models.FileField(upload_to="applications/writing/%Y/%m/", null=True, blank=True)
    photo = models.ImageField(upload_to="applications/photos/%Y/%m/", null=True, blank=True)
    cover_letter = models.TextField(blank=True)
    stage = models.CharField(max_length=20, choices=STAGE_CHOICES, default="applied", db_index=True)
    notes = models.TextField(blank=True, help_text="Internal reviewer notes")
    # Set once a hired application is converted into an Employee record.
    converted_employee = models.OneToOneField(
        Employee, on_delete=models.SET_NULL, null=True, blank=True, related_name="source_application"
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_job_applications"
        ordering = ["-created_at"]
        indexes = [models.Index(fields=["recruitment", "stage"])]

    def __str__(self):
        return f"{self.full_name} → {self.recruitment.position}"


class Onboarding(models.Model):
    """Employee onboarding tasks and tracking."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    employee = models.OneToOneField(Employee, on_delete=models.CASCADE, related_name="onboarding")
    stage = models.CharField(max_length=20, choices=[
        ("pending", "Pending"),
        ("in_progress", "In Progress"),
        ("completed", "Completed"),
    ], default="pending")
    start_date = models.DateField()
    end_date = models.DateField(null=True, blank=True)
    tasks = models.JSONField(default=list)
    checklist = models.JSONField(default=dict)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_onboardings"

    def __str__(self):
        return f"Onboarding: {self.employee}"


class Contract(models.Model):
    """Employee contracts."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name="contracts")
    contract_type = models.CharField(max_length=20, choices=[
        ("full_time", "Full Time"),
        ("part_time", "Part Time"),
        ("contract", "Contract"),
        ("internship", "Internship"),
    ])
    start_date = models.DateField()
    end_date = models.DateField(null=True, blank=True)
    status = models.CharField(max_length=20, choices=[
        ("active", "Active"),
        ("expired", "Expired"),
        ("terminated", "Terminated"),
        ("renewed", "Renewed"),
    ], default="active")
    document = models.FileField(upload_to="contracts/", null=True, blank=True)
    terms = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_contracts"
        ordering = ["-start_date"]

    def __str__(self):
        return f"Contract: {self.employee} - {self.contract_type}"


class Communication(models.Model):
    """Internal HR communications."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    title = models.CharField(max_length=255)
    content = models.TextField()
    communication_type = models.CharField(max_length=20, choices=[
        ("announcement", "Announcement"),
        ("memo", "Memo"),
        ("meeting", "Meeting"),
        ("policy", "Policy"),
    ])
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="hr_communications")
    is_pinned = models.BooleanField(default=False)
    attachments = models.JSONField(default=list)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "hr_communications"
        ordering = ["-is_pinned", "-created_at"]


class Survey(models.Model):
    """HR surveys and feedback collection."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    title = models.CharField(max_length=255)
    description = models.TextField()
    # List of {id, type: short_text|long_text|single_choice|rating, label, options?, required}
    questions = models.JSONField(default=list)
    status = models.CharField(max_length=20, choices=[
        ("draft", "Draft"),
        ("active", "Active"),
        ("closed", "Closed"),
    ], default="draft")
    # Optional fill window. Survey accepts responses only between these (when set).
    open_at = models.DateTimeField(null=True, blank=True)
    close_at = models.DateTimeField(null=True, blank=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="hr_surveys")
    responses = models.JSONField(default=list)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_surveys"
        ordering = ["-created_at"]

    def is_open(self) -> bool:
        """True if the survey currently accepts responses (active + within window)."""
        if self.status != "active":
            return False
        now = timezone.now()
        if self.open_at and now < self.open_at:
            return False
        if self.close_at and now > self.close_at:
            return False
        return True


class SurveyResponse(models.Model):
    """A single respondent's submission to a Survey."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    survey = models.ForeignKey(Survey, on_delete=models.CASCADE, related_name="response_set")
    respondent = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="survey_responses")
    # List of {question_id, value}
    answers = models.JSONField(default=list)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "hr_survey_responses"
        ordering = ["-created_at"]
        constraints = [
            models.UniqueConstraint(fields=["survey", "respondent"], name="uniq_survey_respondent"),
        ]

    def __str__(self):
        return f"Response to {self.survey.title} by {self.respondent}"


class Family(models.Model):
    """Employee family members."""
    RELATIONSHIP_CHOICES = [
        ("spouse", "Spouse"),
        ("child", "Child"),
        ("parent", "Parent"),
        ("sibling", "Sibling"),
        ("other", "Other"),
    ]
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name="family_members")
    name = models.CharField(max_length=100)
    relationship = models.CharField(max_length=20, choices=RELATIONSHIP_CHOICES)
    date_of_birth = models.DateField(null=True, blank=True)
    phone = models.CharField(max_length=20, blank=True)
    email = models.EmailField(blank=True)
    occupation = models.CharField(max_length=100, blank=True)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_families"
        ordering = ["name"]

    def __str__(self):
        return f"{self.name} ({self.relationship}) - {self.employee}"


DEFAULT_ATTENDANCE_SETTINGS = {
    "flexible_hours": {"core_start": "10:00", "core_end": "15:00", "flex_band_hours": 2},
    "tolerance": {
        "clock_in_grace_minutes": 10,
        "clock_out_grace_minutes": 10,
        "early_allowed_minutes": 30,
    },
    "late_rules": {
        "notify_after_minutes": 15,
        "penalty_after_minutes": 30,
        "monthly_allowed_count": 3,
    },
    "overtime": {
        "enabled": True,
        "trigger_after_minutes": 30,
        "weekday_rate": 1.5,
        "weekend_rate": 2,
        "holiday_rate": 3,
    },
    "breaks": {"duration_minutes": 60, "per_shift_count": 1, "auto_deduct": True},
    "geofencing": {"enabled": True, "radius_meters": 100, "enforce": True},
    "gps": {"enabled": True, "accuracy_meters": 50},
    "face": {"enabled": True, "min_confidence": 80, "fallback_selfie": True},
    "auto_checkout": {
        "enabled": False,
        "time": "23:59",
        "note": "Auto-closes open attendance at end of day.",
    },
}


class AttendanceSettings(models.Model):
    """Singleton holding org-wide attendance config."""

    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    settings = models.JSONField(default=dict, blank=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_attendance_settings"
        verbose_name = "Attendance settings"
        verbose_name_plural = "Attendance settings"

    def __str__(self):
        return "Attendance settings"

    @classmethod
    def get_solo(cls) -> "AttendanceSettings":
        obj = cls.objects.first()
        if obj is None:
            obj = cls.objects.create(settings=DEFAULT_ATTENDANCE_SETTINGS)
        elif not obj.settings:
            obj.settings = DEFAULT_ATTENDANCE_SETTINGS
            obj.save(update_fields=["settings", "updated_at"])
        return obj


DEFAULT_PAYROLL_SETTINGS = {
    "officer": {
        "name": "",
        "title": "Payroll Officer",
        "employee_id": "",
    },
    "period": {
        "type": "monthly",
        "cutoff_day": 25,
        "pay_day": 1,
        "pay_day_next_month": True,
    },
    "components": {
        "allowances": [
            {"key": "transport", "label": "Transport Allowance", "default_amount": 0},
            {"key": "meal", "label": "Meal Allowance", "default_amount": 0},
        ],
        "deductions": [
            {"key": "bpjs_kesehatan", "label": "BPJS Kesehatan", "rate": 0.01},
            {"key": "bpjs_jht", "label": "BPJS JHT", "rate": 0.02},
        ],
    },
    "tax": {
        "method": "netto",
        "ptkp_basis": "TK0",
    },
    "bpjs": {
        "kesehatan_employee_rate": 1.0,
        "kesehatan_employer_rate": 4.0,
        "jht_employee_rate": 2.0,
        "jht_employer_rate": 3.7,
        "jp_employee_rate": 1.0,
        "jp_employer_rate": 2.0,
        "jkk_rate": 0.24,
        "jkm_rate": 0.3,
    },
    "thr": {
        "enabled": True,
        "basis": "monthly_salary",
        "prorate_threshold_months": 12,
    },
    "currency": "IDR",
}


class PayrollSettings(models.Model):
    """Singleton holding org-wide payroll config."""

    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    settings = models.JSONField(default=dict, blank=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_payroll_settings"
        verbose_name = "Payroll settings"
        verbose_name_plural = "Payroll settings"

    def __str__(self):
        return "Payroll settings"

    @classmethod
    def get_solo(cls) -> "PayrollSettings":
        obj = cls.objects.first()
        if obj is None:
            obj = cls.objects.create(settings=DEFAULT_PAYROLL_SETTINGS)
        elif not obj.settings:
            obj.settings = DEFAULT_PAYROLL_SETTINGS
            obj.save(update_fields=["settings", "updated_at"])
        return obj


class ShiftTemplate(models.Model):
    """Reusable working time pattern with optional flex hours + employee members."""

    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=100)
    start_time = models.CharField(max_length=5, default="09:00", help_text="HH:MM")
    end_time = models.CharField(max_length=5, default="18:00", help_text="HH:MM")
    working_days = models.CharField(max_length=64, default="Mon-Fri")
    flex_enabled = models.BooleanField(default=False)
    members = models.ManyToManyField(Employee, blank=True, related_name="shift_templates")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "hr_shift_templates"
        ordering = ["name"]
        verbose_name = "Shift template"
        verbose_name_plural = "Shift templates"

    def __str__(self):
        return self.name