import json
import logging
from datetime import datetime
from typing import Dict, Any, Optional
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.http import HttpRequest
from .models import Source, JobPosting, IntegrationLog
from .serializers import JobPostingSerializer

logger = logging.getLogger(__name__)


class ERPIntegrationService:
    """
    Service to handle integration between external ERP system and ATS
    """

    def __init__(self, source: Source):
        self.source = source
        self.logger = logging.getLogger(f'{__name__}.{source.name}')

    def validate_request(self, request: HttpRequest) -> tuple[bool, str]:
        """
        Validate the incoming request from ERP system
        Returns: (is_valid, error_message)
        """
        # Check if source is active
        if not self.source.is_active:
            return False, "Source is not active"

        # Check if trusted IPs are configured and validate request IP
        if self.source.trusted_ips:
            client_ip = self.get_client_ip(request)
            trusted_ips = [ip.strip() for ip in self.source.trusted_ips.split(',')]

            if client_ip not in trusted_ips:
                self.logger.warning(f"Request from untrusted IP: {client_ip}")
                return False, f"Request from untrusted IP: {client_ip}"

        # Check API key if provided
        if self.source.api_key:
            api_key = request.headers.get('X-API-Key') or request.GET.get('api_key')
            if not api_key or api_key != self.source.api_key:
                self.logger.warning("Invalid or missing API key")
                return False, "Invalid or missing API key"

        return True, ""

    def log_integration_request(self, request: HttpRequest, action: str, **kwargs):
        """
        Log the integration request/response
        """
        IntegrationLog.objects.create(
            source=self.source,
            action=action,
            endpoint=request.path,
            method=request.method,
            request_data=self.get_request_data(request),
            ip_address=self.get_client_ip(request),
            user_agent=request.META.get('HTTP_USER_AGENT', ''),
            **kwargs
        )

    def create_job_from_erp(self, request_data: Dict[str, Any]) -> tuple[Optional[JobPosting], str]:
        """
        Create a JobPosting from ERP request data
        Returns: (job, error_message)
        """
        try:
            # Map ERP fields to JobPosting fields
            job_data = {
                'title': request_data.get('title', '').strip(),
                'department': request_data.get('department', '').strip(),
                'job_type': self.map_job_type(request_data.get('job_type', 'FULL_TIME')),
                'workplace_type': self.map_workplace_type(request_data.get('workplace_type', 'ON_SITE')),
                'location_city': request_data.get('location_city', '').strip(),
                'location_state': request_data.get('location_state', '').strip(),
                'location_country': request_data.get('location_country', 'United States').strip(),
                'description': request_data.get('description', '').strip(),
                'qualifications': request_data.get('qualifications', '').strip(),
                'salary_range': request_data.get('salary_range', '').strip(),
                'benefits': request_data.get('benefits', '').strip(),
                'application_url': request_data.get('application_url', '').strip(),
                'application_deadline': self.parse_date(request_data.get('application_deadline')),
                'application_instructions': request_data.get('application_instructions', '').strip(),
                'created_by': f'ERP Integration: {self.source.name}',
                'status': 'DRAFT' if request_data.get('auto_publish', False) else 'DRAFT',
                'source': self.source
            }

            # Validate required fields
            if not job_data['title']:
                return None, "Job title is required"


            # Create the job
            job = JobPosting(**job_data)
            job.save()

            self.logger.info(f"Created job {job.internal_job_id} from ERP integration")
            return job, ""

        except Exception as e:
            error_msg = f"Error creating job from ERP: {str(e)}"
            self.logger.error(error_msg)
            return None, error_msg

    def update_job_from_erp(self, job_id: str, request_data: Dict[str, Any]) -> tuple[Optional[JobPosting], str]:
        """
        Update an existing JobPosting from ERP request data
        Returns: (job, error_message)
        """
        try:
            job = JobPosting.objects.get(internal_job_id=job_id)

            # Update fields from ERP data
            updatable_fields = [
                'title', 'department', 'job_type', 'workplace_type',
                'location_city', 'location_state', 'location_country',
                'description', 'qualifications', 'salary_range', 'benefits',
                'application_url', 'application_deadline', 'application_instructions',
                'status'
            ]

            for field in updatable_fields:
                if field in request_data:
                    value = request_data[field]

                    # Special handling for date fields
                    if field == 'application_deadline':
                        value = self.parse_date(value)

                    setattr(job, field, value)

            # Update source if provided
            if 'source_id' in request_data:
                try:
                    source = Source.objects.get(id=request_data['source_id'])
                    job.source = source
                except Source.DoesNotExist:
                    pass

            job.save()

            self.logger.info(f"Updated job {job.internal_job_id} from ERP integration")
            return job, ""

        except JobPosting.DoesNotExist:
            return None, f"Job with ID {job_id} not found"
        except Exception as e:
            error_msg = f"Error updating job from ERP: {str(e)}"
            self.logger.error(error_msg)
            return None, error_msg

    def validate_erp_data(self, data: Dict[str, Any]) -> tuple[bool, str]:
        """
        Validate ERP request data structure
        Returns: (is_valid, error_message)
        """
        required_fields = ['title']

        for field in required_fields:
            if field not in data or not data[field]:
                return False, f"Required field '{field}' is missing or empty"

        # Validate URL format
        if data.get('application_url'):
            from django.core.validators import URLValidator
            from django.core.exceptions import ValidationError as DjangoValidationError

            try:
                URLValidator()(data['application_url'])
            except DjangoValidationError:
                return False, "Invalid application URL format"

        # Validate job type
        if 'job_type' in data and data['job_type']:
            valid_job_types = dict(JobPosting.JOB_TYPES)
            if data['job_type'] not in valid_job_types:
                return False, f"Invalid job type: {data['job_type']}"

        # Validate workplace type
        if 'workplace_type' in data and data['workplace_type']:
            valid_workplace_types = dict(JobPosting.WORKPLACE_TYPES)
            if data['workplace_type'] not in valid_workplace_types:
                return False, f"Invalid workplace type: {data['workplace_type']}"

        return True, ""

    # Helper methods
    def get_client_ip(self, request: HttpRequest) -> str:
        """Get the client IP address from request"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

    def get_request_data(self, request: HttpRequest) -> Dict[str, Any]:
        """Get request data from request object"""
        if request.method == 'GET':
            return dict(request.GET)
        elif request.method in ['POST', 'PUT', 'PATCH']:
            try:
                if request.content_type == 'application/json':
                    return json.loads(request.body.decode('utf-8'))
                else:
                    return dict(request.POST)
            except:
                return {}
        return {}

    def parse_date(self, date_str: str) -> Optional[datetime.date]:
        """Parse date string from ERP"""
        if not date_str:
            return None

        try:
            # Try different date formats
            date_formats = [
                '%Y-%m-%d',
                '%m/%d/%Y',
                '%d/%m/%Y',
                '%Y-%m-%d %H:%M:%S',
                '%m/%d/%Y %H:%M:%S',
                '%d/%m/%Y %H:%M:%S'
            ]

            for fmt in date_formats:
                try:
                    dt = datetime.strptime(date_str, fmt)
                    if fmt.endswith('%H:%M:%S'):
                        return dt.date()
                    return dt.date()
                except ValueError:
                    continue

            # If no format matches, try to parse with dateutil
            from dateutil import parser
            dt = parser.parse(date_str)
            return dt.date()

        except Exception as e:
            self.logger.warning(f"Could not parse date '{date_str}': {str(e)}")
            return None

    def map_job_type(self, erp_job_type: str) -> str:
        """Map ERP job type to ATS job type"""
        mapping = {
            'full-time': 'FULL_TIME',
            'part-time': 'PART_TIME',
            'contract': 'CONTRACT',
            'internship': 'INTERNSHIP',
            'faculty': 'FACULTY',
            'temporary': 'TEMPORARY',
        }

        return mapping.get(erp_job_type.lower(), 'FULL_TIME')

    def map_workplace_type(self, erp_workplace_type: str) -> str:
        """Map ERP workplace type to ATS workplace type"""
        mapping = {
            'onsite': 'ON_SITE',
            'on-site': 'ON_SITE',
            'remote': 'REMOTE',
            'hybrid': 'HYBRID',
        }

        return mapping.get(erp_workplace_type.lower(), 'ON_SITE')
