import os

from django.conf import settings
from django.db import models
from django.urls import reverse
from django.utils.text import slugify

from .status import (
    job_accepts_applications,
    job_is_closed,
    job_is_expired,
    job_is_manually_closed,
    job_is_ongoing,
    job_needs_deadline_to_reactivate,
)
from .content import build_job_display_content


class JobCategory(models.Model):
    number = models.PositiveSmallIntegerField(default=0, help_text='Official PEZAJOB category number (1–40)')
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=120, unique=True)
    icon = models.CharField(max_length=50, blank=True, default='fa-briefcase')
    sort_order = models.PositiveSmallIntegerField(default=0)
    is_active = models.BooleanField(default=True)

    class Meta:
        ordering = ['sort_order', 'name']
        verbose_name_plural = 'job categories'

    def __str__(self):
        return self.numbered_name if self.number else self.name

    @property
    def numbered_name(self):
        if self.number:
            return f'{self.number}. {self.name}'
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)

    @classmethod
    def active(cls):
        return cls.objects.filter(is_active=True).order_by('number', 'sort_order', 'name')


class Job(models.Model):
    class JobType(models.TextChoices):
        FULL_TIME = 'full_time', 'Full-Time'
        PART_TIME = 'part_time', 'Part-Time'
        CONTRACT = 'contract', 'Contract'
        REMOTE = 'remote', 'Remote'
        INTERNSHIP = 'internship', 'Internship'
        GRADUATE_TRAINEE = 'graduate_trainee', 'Graduate Trainee'
        FREELANCER = 'freelancer', 'Freelancer'
        VOLUNTEER = 'volunteer', 'Volunteer'

    class ExperienceLevel(models.TextChoices):
        ENTRY = 'entry', 'Entry Level (0-2 years)'
        MID = 'mid', 'Mid Level (3-5 years)'
        SENIOR = 'senior', 'Senior (6-9 years)'
        EXECUTIVE = 'executive', 'Executive (10+ years)'

    class Badge(models.TextChoices):
        NONE = '', 'None'
        HOT = 'hot', 'Hot'
        NEW = 'new', 'New'
        PREMIUM = 'premium', 'Premium'

    class ApplicationMethod(models.TextChoices):
        PEZAJOB = 'ats', 'Apply through PezaJob'
        EXTERNAL = 'email', 'Apply through an external website/email'

    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=220, unique=True)
    company_name = models.CharField(max_length=150)
    company_slug = models.SlugField(max_length=170, blank=True)
    company_initials = models.CharField(max_length=4)
    company_logo_gradient = models.CharField(
        max_length=120,
        default='linear-gradient(135deg,#2c3e50,#34495e)',
    )
    company_logo = models.ImageField(
        upload_to='jobs/logos/%Y/%m/',
        max_length=255,
        blank=True,
        null=True,
        help_text='Optional company logo for this listing',
    )
    company_industry = models.CharField(max_length=100, blank=True)
    location = models.CharField(max_length=120)
    location_country = models.CharField(max_length=2, default='ZM', blank=True)
    location_regions = models.JSONField(default=list, blank=True)
    location_city = models.CharField(
        max_length=120,
        blank=True,
        help_text='Primary city or town (optional for multi-region roles).',
    )
    job_type = models.CharField(max_length=20, choices=JobType.choices, default=JobType.FULL_TIME)
    experience_level = models.CharField(
        max_length=20,
        choices=ExperienceLevel.choices,
        default=ExperienceLevel.MID,
    )
    experience_years_min = models.PositiveSmallIntegerField(
        null=True,
        blank=True,
        help_text='Minimum years of experience required.',
    )
    experience_years_max = models.PositiveSmallIntegerField(
        null=True,
        blank=True,
        help_text='Maximum years of experience required.',
    )
    experience_required = models.CharField(
        max_length=120,
        blank=True,
        help_text='Manual experience requirement, e.g. 3+ years or Prior IT experience',
    )
    category = models.ForeignKey(
        JobCategory,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='jobs',
    )
    positions_total = models.PositiveSmallIntegerField(
        default=1,
        help_text='Number of people being recruited for this role.',
    )
    positions_filled = models.PositiveSmallIntegerField(
        default=0,
        help_text='How many positions have been filled (updated when candidates are marked hired).',
    )
    salary_min = models.PositiveIntegerField(null=True, blank=True)
    salary_max = models.PositiveIntegerField(null=True, blank=True)
    badge = models.CharField(max_length=20, choices=Badge.choices, blank=True, default=Badge.NONE)
    summary = models.TextField(blank=True)
    description = models.TextField()
    responsibilities = models.TextField(help_text='One item per line')
    requirements = models.TextField(help_text='One item per line')
    nice_to_have = models.TextField(blank=True, help_text='One item per line')
    benefits = models.TextField(blank=True, help_text='One benefit per line')
    application_method = models.CharField(
        max_length=10,
        choices=ApplicationMethod.choices,
        default=ApplicationMethod.EXTERNAL,
        help_text='Legacy primary application method.',
    )
    application_methods = models.JSONField(
        default=list,
        blank=True,
        help_text='Selected application channels for this job (PezaJob, external, or both).',
    )
    application_candidate_fields = models.JSONField(
        default=list,
        blank=True,
        help_text='Candidate information required for PezaJob applications.',
    )
    application_documents = models.JSONField(
        default=list,
        blank=True,
        help_text='Documents required for PezaJob applications.',
    )
    screening_questions_enabled = models.BooleanField(default=False)
    screening_questions = models.JSONField(
        default=list,
        blank=True,
        help_text='Additional screening questions for PezaJob applicants.',
    )
    how_to_apply = models.TextField(blank=True)
    cover_image_url = models.URLField(blank=True)
    deadline = models.DateField(null=True, blank=True)
    is_active = models.BooleanField(default=True)
    is_featured = models.BooleanField(default=False)
    posted_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='posted_jobs',
    )
    views_count = models.PositiveIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return f'{self.title} at {self.company_name}'

    def save(self, *args, **kwargs):
        from home.textclean import strip_emoji

        self.title = strip_emoji(self.title)
        for name in ('description', 'responsibilities', 'requirements', 'nice_to_have', 'benefits'):
            setattr(self, name, strip_emoji(getattr(self, name, ''), bullets=True))
        for name in ('summary', 'how_to_apply'):
            setattr(self, name, strip_emoji(getattr(self, name, '')))
        if not self.slug:
            base = slugify(self.title)
            slug = base
            counter = 1
            while Job.objects.filter(slug=slug).exclude(pk=self.pk).exists():
                slug = f'{base}-{counter}'
                counter += 1
            self.slug = slug
        if not self.company_slug:
            self.company_slug = slugify(self.company_name)
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('jobs:detail', kwargs={'slug': self.slug})

    @property
    def salary_display(self):
        if self.salary_min and self.salary_max:
            if self.salary_min == self.salary_max:
                return f'K {self.salary_min:,}'
            return f'K {self.salary_min:,} – {self.salary_max:,}'
        if self.salary_min:
            return f'K {self.salary_min:,}'
        if self.salary_max:
            return f'K {self.salary_max:,}'
        return 'Negotiable'

    @property
    def experience_display(self):
        if self.experience_years_min is not None and self.experience_years_max is not None:
            from .experience_utils import format_experience_display

            return format_experience_display(self.experience_years_min, self.experience_years_max)
        if self.experience_required:
            return self.experience_required
        return self.get_experience_level_display()

    @property
    def list_items(self):
        return build_job_display_content(self)['list_items']

    @property
    def display_content(self):
        return build_job_display_content(self)

    @property
    def is_ongoing(self):
        return job_is_ongoing(self.deadline)

    @property
    def is_expired(self):
        return job_is_expired(self.deadline)

    @property
    def is_manually_closed(self):
        return job_is_manually_closed(is_active=self.is_active, deadline=self.deadline)

    @property
    def is_closed(self):
        return job_is_closed(is_active=self.is_active, deadline=self.deadline)

    def get_application_methods(self):
        from .application_utils import application_methods_for_job

        return application_methods_for_job(self)

    @property
    def accepts_pezajob_applications(self):
        return self.ApplicationMethod.PEZAJOB in self.get_application_methods()

    @property
    def accepts_external_applications(self):
        return self.ApplicationMethod.EXTERNAL in self.get_application_methods()

    @property
    def accepts_both_application_methods(self):
        return self.accepts_pezajob_applications and self.accepts_external_applications

    def application_candidate_fields_list(self):
        from .application_requirements import normalize_candidate_fields

        if not self.accepts_pezajob_applications:
            return []
        return normalize_candidate_fields(self.application_candidate_fields)

    def application_documents_list(self):
        from .application_requirements import normalize_documents

        if not self.accepts_pezajob_applications:
            return []
        return normalize_documents(self.application_documents)

    def screening_questions_list(self):
        from .application_requirements import parse_screening_questions

        if not self.screening_questions_enabled:
            return []
        return parse_screening_questions(self.screening_questions)

    @property
    def positions_remaining(self):
        return max(0, self.positions_total - self.positions_filled)

    @property
    def positions_display(self):
        remaining = self.positions_remaining
        if self.positions_total <= 1:
            if remaining == 0:
                return 'Position filled'
            return '1 position open'
        if remaining == 0:
            return f'All {self.positions_total} positions filled'
        if remaining == 1:
            return f'1 of {self.positions_total} positions open'
        return f'{remaining} of {self.positions_total} positions open'

    @property
    def accepts_applications(self):
        return (
            job_accepts_applications(is_active=self.is_active, deadline=self.deadline)
            and self.positions_remaining > 0
        )

    @property
    def needs_deadline_to_reactivate(self):
        return job_needs_deadline_to_reactivate(is_active=self.is_active, deadline=self.deadline)

    @property
    def logo_url(self):
        if self.company_logo:
            try:
                return self.company_logo.url
            except (ValueError, OSError):
                pass
        if self.company_slug:
            if not hasattr(self, '_company_logo_cache'):
                from companies.models import Company

                company = Company.objects.filter(slug=self.company_slug).select_related('owner').first()
                self._company_logo_cache = company.display_logo_url if company else ''
            if self._company_logo_cache:
                return self._company_logo_cache
        poster = self.posted_by if self.posted_by_id else None
        if poster is not None and getattr(poster, 'role', '') == 'company' and getattr(poster, 'avatar', None):
            try:
                if poster.avatar:
                    return poster.avatar.url
            except (ValueError, OSError):
                pass
        return ''

    @property
    def has_logo_image(self):
        return bool(self.logo_url)

    @property
    def image_attachments(self):
        return self.attachments.filter(kind=JobAttachment.Kind.IMAGE)

    @property
    def pdf_attachments(self):
        return self.attachments.filter(kind=JobAttachment.Kind.PDF)


class JobAttachment(models.Model):
    class Kind(models.TextChoices):
        IMAGE = 'image', 'Image'
        PDF = 'pdf', 'Document'

    job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='attachments')
    file = models.FileField(upload_to='jobs/attachments/%Y/%m/', max_length=255)
    kind = models.CharField(max_length=10, choices=Kind.choices)
    title = models.CharField(max_length=200, blank=True)
    sort_order = models.PositiveSmallIntegerField(default=0)
    uploaded_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['sort_order', 'id']

    def __str__(self):
        return f'{self.get_kind_display()} for {self.job.title}'

    @property
    def display_title(self):
        if self.title:
            return self.title
        return os.path.basename(self.file.name)

    @property
    def is_image(self):
        return self.kind == self.Kind.IMAGE

    @property
    def is_pdf(self):
        return self.kind == self.Kind.PDF

    @property
    def icon_class(self):
        if self.is_image:
            return 'fa-image'
        ext = os.path.splitext(self.file.name.lower())[1]
        if ext in ('.doc', '.docx'):
            return 'fa-file-word'
        return 'fa-file-pdf'


class JobApplication(models.Model):
    class Status(models.TextChoices):
        PENDING = 'pending', 'Pending'
        REVIEW = 'review', 'Under Review'
        SHORTLISTED = 'shortlisted', 'Shortlisted'
        INTERVIEW = 'interview', 'Interview'
        REJECTED = 'rejected', 'Rejected'
        HIRED = 'hired', 'Hired'

    job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='applications')
    applicant = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='job_applications',
    )
    cover_letter = models.TextField(blank=True)
    cv_file = models.FileField(upload_to='applications/cv/', max_length=255, blank=True, null=True)
    portfolio_file = models.FileField(upload_to='applications/portfolio/', max_length=255, blank=True, null=True)
    qualification_file = models.FileField(upload_to='applications/qualifications/', max_length=255, blank=True, null=True)
    other_document = models.FileField(upload_to='applications/other/', max_length=255, blank=True, null=True)
    screening_answers = models.JSONField(default=dict, blank=True)
    cv_parsed_text = models.TextField(blank=True, help_text='Extracted plain text from uploaded CV')
    cv_extracted_skills = models.TextField(blank=True, help_text='Skills detected in CV, one per line')
    cv_parsed_at = models.DateTimeField(null=True, blank=True)
    linkedin_url = models.URLField(blank=True)
    status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
    match_score = models.PositiveSmallIntegerField(default=0, help_text='0–100 skill match vs job requirements')
    rating = models.PositiveSmallIntegerField(null=True, blank=True, help_text='Employer rating 1–5')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']
        unique_together = [('job', 'applicant')]

    def __str__(self):
        return f'{self.applicant} - {self.job.title}'

    @property
    def screening_qa(self):
        answers = self.screening_answers or {}
        return [
            {
                'question': question['text'],
                'answer': (answers.get(question['id']) or '').strip() or '—',
                'required': question.get('required'),
            }
            for question in self.job.screening_questions_list()
        ]

    @property
    def status_badge_style(self):
        return {
            self.Status.PENDING: 'background:#f5f5f5;color:#616161;',
            self.Status.REVIEW: 'background:#e3f2fd;color:#1565c0;',
            self.Status.SHORTLISTED: 'background:#e8f5e9;color:#2e7d32;',
            self.Status.INTERVIEW: 'background:#f3e5f5;color:#7b1fa2;',
            self.Status.REJECTED: 'background:#fce4ec;color:#c62828;',
            self.Status.HIRED: 'background:#fff8e1;color:#f57f17;',
        }.get(self.status, 'background:#f5f5f5;color:#616161;')

    @property
    def status_css_class(self):
        return {
            self.Status.PENDING: 's-new',
            self.Status.REVIEW: 's-review',
            self.Status.SHORTLISTED: 's-shortlisted',
            self.Status.INTERVIEW: 's-interview',
            self.Status.REJECTED: 's-rejected',
            self.Status.HIRED: 's-hired',
        }.get(self.status, 's-new')

    @property
    def cv_skill_list(self):
        return [line.strip() for line in self.cv_extracted_skills.splitlines() if line.strip()]

    @property
    def match_level(self):
        if self.match_score >= 70:
            return 'high'
        if self.match_score >= 40:
            return 'mid'
        return 'low'


class JobAlertLog(models.Model):
    """Tracks priority job alert emails sent to paid subscribers."""
    job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='alert_logs')
    recipient = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='job_alerts_received',
    )
    match_score = models.PositiveSmallIntegerField(default=0)
    sent_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-sent_at']
        unique_together = [('job', 'recipient')]
        verbose_name = 'Job alert log'
        verbose_name_plural = 'Job alert logs'

    def __str__(self):
        return f'Alert: {self.job.title} to {self.recipient.email}'


class SavedJob(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='saved_jobs')
    job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='saved_by')
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = [('user', 'job')]
        ordering = ['-created_at']

    def __str__(self):
        return f'{self.user} saved {self.job.title}'


class JobView(models.Model):
    """Historical job view record from legacy store_itemviewcount* tables."""
    job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='view_logs')
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='job_views',
    )
    ip_address = models.CharField(max_length=45, blank=True)
    is_unique = models.BooleanField(default=False)
    viewed_at = models.DateTimeField()

    class Meta:
        ordering = ['-viewed_at']
        indexes = [
            models.Index(fields=['job', '-viewed_at']),
        ]

    def __str__(self):
        return f'View: {self.job_id} at {self.viewed_at:%Y-%m-%d}'
