import json
import logging
import requests
from datetime import datetime
from typing import Dict, Any, List, Optional, Tuple
from django.utils import timezone
from django.conf import settings
from django.core.files.base import ContentFile
from django.http import HttpRequest
from .models import Source, Candidate, JobPosting, IntegrationLog

logger = logging.getLogger(__name__)


class CandidateSyncService:
    """
    Service to handle synchronization of hired candidates to external sources
    """

    def __init__(self):
        self.logger = logging.getLogger(__name__)

    def sync_hired_candidates_to_all_sources(self, job: JobPosting) -> Dict[str, Any]:
        """
        Sync all hired candidates for a job to all active external sources

        Returns: Dictionary with sync results for each source
        """
        results = {
            'total_candidates': 0,
            'successful_syncs': 0,
            'failed_syncs': 0,
            'source_results': {},
            'sync_time': timezone.now().isoformat()
        }

        # Get all hired candidates for this job
        hired_candidates = list(job.hired_candidates.select_related('job'))

        results['total_candidates'] = len(hired_candidates)

        if not hired_candidates:
            self.logger.info(f"No hired candidates found for job {job.title}")
            return results

        # Get all active sources that support outbound sync
        active_sources = Source.objects.filter(
            is_active=True,
            sync_endpoint__isnull=False
        ).exclude(sync_endpoint='')

        if not active_sources:
            self.logger.warning("No active sources with sync endpoints configured")
            return results

        # Sync to each source
        for source in active_sources:
            try:
                source_result = self.sync_to_source(source, hired_candidates, job)
                results['source_results'][source.name] = source_result

                if source_result['success']:
                    results['successful_syncs'] += 1
                else:
                    results['failed_syncs'] += 1

            except Exception as e:
                error_msg = f"Unexpected error syncing to {source.name}: {str(e)}"
                self.logger.error(error_msg)
                results['source_results'][source.name] = {
                    'success': False,
                    'error': error_msg,
                    'candidates_synced': 0
                }
                results['failed_syncs'] += 1

        return results

    def sync_to_source(self, source: Source, candidates: List[Candidate], job: JobPosting) -> Dict[str, Any]:
        """
        Sync candidates to a specific external source

        Returns: Dictionary with sync result for this source
        """
        result = {
            'success': False,
            'error': None,
            'candidates_synced': 0,
            'candidates_failed': 0,
            'candidate_results': []
        }

        try:
            # Prepare headers for the request
            headers = self._prepare_headers(source)

            # Sync each candidate
            for candidate in candidates:
                try:
                    candidate_data = self._format_candidate_data(candidate, job)
                    sync_result = self._send_candidate_to_source(source, candidate_data, headers)

                    result['candidate_results'].append({
                        'candidate_id': candidate.id,
                        'candidate_name': candidate.name,
                        'success': sync_result['success'],
                        'error': sync_result.get('error'),
                        'response_data': sync_result.get('response_data')
                    })

                    if sync_result['success']:
                        result['candidates_synced'] += 1
                    else:
                        result['candidates_failed'] += 1

                except Exception as e:
                    error_msg = f"Error syncing candidate {candidate.name}: {str(e)}"
                    self.logger.error(error_msg)
                    result['candidate_results'].append({
                        'candidate_id': candidate.id,
                        'candidate_name': candidate.name,
                        'success': False,
                        'error': error_msg
                    })
                    result['candidates_failed'] += 1

            # Consider sync successful if at least one candidate was synced
            result['success'] = result['candidates_synced'] > 0

            # Log the sync operation
            self._log_sync_operation(source, result, len(candidates))

        except Exception as e:
            error_msg = f"Failed to sync to source {source.name}: {str(e)}"
            self.logger.error(error_msg)
            result['error'] = error_msg

        return result

    def _prepare_headers(self, source: Source) -> Dict[str, str]:
        """Prepare HTTP headers for the sync request"""
        headers = {
            'Content-Type': 'application/json',
            'User-Agent': f'KAAUH-ATS-Sync/1.0'
        }

        # Add API key if configured
        if source.api_key:
            headers['X-API-Key'] = source.api_key

        # Add custom headers if any
        if hasattr(source, 'custom_headers') and source.custom_headers:
            try:
                custom_headers = json.loads(source.custom_headers)
                headers.update(custom_headers)
            except json.JSONDecodeError:
                self.logger.warning(f"Invalid custom_headers JSON for source {source.name}")

        return headers

    def _format_candidate_data(self, candidate: Candidate, job: JobPosting) -> Dict[str, Any]:
        """Format candidate data for external source"""
        data = {
            'candidate': {
                'id': candidate.id,
                'slug': candidate.slug,
                'first_name': candidate.first_name,
                'last_name': candidate.last_name,
                'full_name': candidate.name,
                'email': candidate.email,
                'phone': candidate.phone,
                'address': candidate.address,
                # 'applied_at': candidate.created_at.isoformat(),
                # 'hired_date': candidate.offer_date.isoformat() if candidate.offer_date else None,
                # 'join_date': candidate.join_date.isoformat() if candidate.join_date else None,
            },
            # 'job': {
            #     'id': job.id,
            #     'internal_job_id': job.internal_job_id,
            #     'title': job.title,
            #     'department': job.department,
            #     'job_type': job.job_type,
            #     'workplace_type': job.workplace_type,
            #     'location': job.get_location_display(),
            # },
            # 'ai_analysis': {
            #     'match_score': candidate.match_score,
            #     'years_of_experience': candidate.years_of_experience,
            #     'screening_rating': candidate.screening_stage_rating,
            #     'professional_category': candidate.professional_category,
            #     'top_skills': candidate.top_3_keywords,
            #     'strengths': candidate.strengths,
            #     'weaknesses': candidate.weaknesses,
            #     'recommendation': candidate.recommendation,
            #     'job_fit_narrative': candidate.job_fit_narrative,
            # },
            # 'sync_metadata': {
            #     'synced_at': timezone.now().isoformat(),
            #     'sync_source': 'KAAUH-ATS',
            #     'sync_version': '1.0'
            # }
        }

        # # Add resume information if available
        # if candidate.resume:
        #     data['candidate']['resume'] = {
        #         'filename': candidate.resume.name,
        #         'size': candidate.resume.size,
        #         'url': candidate.resume.url if hasattr(candidate.resume, 'url') else None
        #     }

        # # Add additional AI analysis data if available
        # if candidate.ai_analysis_data:
        #     data['ai_analysis']['full_analysis'] = candidate.ai_analysis_data

        return data

    def _send_candidate_to_source(self, source: Source, candidate_data: Dict[str, Any], headers: Dict[str, str]) -> Dict[str, Any]:
        """
        Send candidate data to external source

        Returns: Dictionary with send result
        """
        result = {
            'success': False,
            'error': None,
            'response_data': None,
            'status_code': None
        }

        try:
            # Determine HTTP method (default to POST)
            method = getattr(source, 'sync_method', 'POST').upper()

            # Prepare request data
            json_data = json.dumps(candidate_data)

            # Make the HTTP request
            if method == 'POST':
                response = requests.post(
                    source.sync_endpoint,
                    data=json_data,
                    headers=headers,
                    timeout=30
                )
            elif method == 'PUT':
                response = requests.put(
                    source.sync_endpoint,
                    data=json_data,
                    headers=headers,
                    timeout=30
                )
            else:
                raise ValueError(f"Unsupported HTTP method: {method}")

            result['status_code'] = response.status_code
            result['response_data'] = response.text

            # Check if request was successful
            if response.status_code in [200, 201, 202]:
                try:
                    response_json = response.json()
                    result['response_data'] = response_json
                    result['success'] = True
                except json.JSONDecodeError:
                    # If response is not JSON, still consider it successful if status code is good
                    result['success'] = True
            else:
                result['error'] = f"HTTP {response.status_code}: {response.text}"

        except requests.exceptions.Timeout:
            result['error'] = "Request timeout"
        except requests.exceptions.ConnectionError:
            result['error'] = "Connection error"
        except requests.exceptions.RequestException as e:
            result['error'] = f"Request error: {str(e)}"
        except Exception as e:
            result['error'] = f"Unexpected error: {str(e)}"

        return result

    def _log_sync_operation(self, source: Source, result: Dict[str, Any], total_candidates: int):
        """Log the sync operation to IntegrationLog"""
        try:
            IntegrationLog.objects.create(
                source=source,
                action='SYNC',
                endpoint=source.sync_endpoint,
                method=getattr(source, 'sync_method', 'POST'),
                request_data={
                    'total_candidates': total_candidates,
                    'candidates_synced': result['candidates_synced'],
                    'candidates_failed': result['candidates_failed']
                },
                response_data=result,
                status_code='200' if result['success'] else '400',
                error_message=result.get('error'),
                ip_address='127.0.0.1',  # Internal sync
                user_agent='KAAUH-ATS-Sync/1.0'
            )
        except Exception as e:
            self.logger.error(f"Failed to log sync operation: {str(e)}")

    def test_source_connection(self, source: Source) -> Dict[str, Any]:
        """
        Test connection to an external source

        Returns: Dictionary with test result
        """
        result = {
            'success': False,
            'error': None,
            'response_time': None,
            'status_code': None
        }

        try:
            headers = self._prepare_headers(source)
            test_data = {
                'test': True,
                'timestamp': timezone.now().isoformat(),
                'source': 'KAAUH-ATS Connection Test'
            }

            start_time = datetime.now()

            # Use GET method for testing if available, otherwise POST
            test_method = getattr(source, 'test_method', 'GET').upper()

            if test_method == 'GET':
                response = requests.get(
                    source.sync_endpoint,
                    headers=headers,
                    timeout=10
                )
            else:
                response = requests.post(
                    source.sync_endpoint,
                    data=json.dumps(test_data),
                    headers=headers,
                    timeout=10
                )

            end_time = datetime.now()
            result['response_time'] = (end_time - start_time).total_seconds()
            result['status_code'] = response.status_code

            if response.status_code in [200, 201, 202]:
                result['success'] = True
            else:
                result['error'] = f"HTTP {response.status_code}: {response.text}"

        except requests.exceptions.Timeout:
            result['error'] = "Connection timeout"
        except requests.exceptions.ConnectionError:
            result['error'] = "Connection failed"
        except Exception as e:
            result['error'] = f"Test failed: {str(e)}"

        return result
