import io
from collections import Counter
from django.db.models import Count, Q
from django.http import HttpResponse
from rest_framework import viewsets, permissions, generics, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.filters import SearchFilter
from rest_framework.pagination import PageNumberPagination
from django_filters.rest_framework import DjangoFilterBackend
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.worksheet.datavalidation import DataValidation
from .models import Job, JobFavorite
from .serializers import JobListSerializer, JobDetailSerializer, JobFavoriteSerializer
from .filters import JobFilter
from apps.accounts.permissions import IsAdminOrSuperAdmin, IsSeeker
from apps.organizations.models import Organization
from apps.applications.models import Application
from .school_data import classify_school


class JobPublicPagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = 'page_size'
    max_page_size = 500


class JobPublicViewSet(viewsets.ReadOnlyModelViewSet):
    """公开只读，仅返回已发布职位"""
    queryset = Job.objects.filter(status='published').select_related('organization')
    filterset_class = JobFilter
    filter_backends = [DjangoFilterBackend, SearchFilter]
    search_fields = ['title', 'description', 'location']
    permission_classes = [permissions.AllowAny]
    pagination_class = JobPublicPagination

    def get_serializer_class(self):
        if self.action == 'retrieve':
            return JobDetailSerializer
        return JobListSerializer

    @action(detail=True, methods=['post'], permission_classes=[IsSeeker])
    def favorite(self, request, pk=None):
        job = self.get_object()
        fav, created = JobFavorite.objects.get_or_create(user=request.user, job=job)
        if not created:
            fav.delete()
            return Response({'collected': False})
        return Response({'collected': True})


class MyFavoritesView(generics.ListAPIView):
    """求职者的收藏列表"""
    serializer_class = JobFavoriteSerializer
    permission_classes = [IsSeeker]

    def get_queryset(self):
        return JobFavorite.objects.filter(user=self.request.user).select_related(
            'job', 'job__organization'
        )


class JobManageViewSet(viewsets.ModelViewSet):
    """管理端：公司管理员管理本公司职位"""
    permission_classes = [IsAdminOrSuperAdmin]

    def get_serializer_class(self):
        if self.action in ['retrieve', 'create', 'update', 'partial_update']:
            return JobDetailSerializer
        return JobListSerializer

    def get_queryset(self):
        user = self.request.user
        if user.is_superadmin:
            return Job.objects.all().select_related('organization')
        # 防御 organization 为空的情况
        if not user.organization_id:
            return Job.objects.none()
        return Job.objects.filter(organization=user.organization).select_related('organization')

    def perform_create(self, serializer):
        if self.request.user.is_admin:
            # Admin 强制使用自己公司，忽略请求体中的 organization_id
            serializer.save(organization=self.request.user.organization)
        else:
            # 超管需要在请求体中提供 organization_id
            serializer.save()

    @action(detail=False, methods=['get'], url_path='template')
    def download_template(self, request):
        """下载职位导入Excel模板"""
        wb = openpyxl.Workbook()
        ws = wb.active
        ws.title = '职位导入模板'

        # 表头定义: (列名, 是否必填, 列宽, 示例值)
        columns = [
            ('职位名称', True, 20, 'Python开发工程师'),
            ('职位类别', True, 15, '技术'),
            ('工作地点', True, 15, '北京'),
            ('薪资范围', True, 15, '15k-25k'),
            ('学历要求', True, 15, '本科及以下'),
            ('招聘人数', False, 12, '3'),
            ('职位描述', False, 40, '负责后端系统开发与维护'),
            ('状态', False, 12, '已发布'),
        ]
        if request.user.is_superadmin:
            columns.insert(0, ('所属公司名称', True, 25, '华远科技有限公司'))

        col_map_by_name = {name: idx for idx, (name, _, _, _) in enumerate(columns, 1)}

        header_font = Font(bold=True, color='FFFFFF', size=11)
        required_fill = PatternFill(start_color='2196F3', end_color='2196F3', fill_type='solid')
        optional_fill = PatternFill(start_color='90CAF9', end_color='90CAF9', fill_type='solid')
        thin_border = Border(
            left=Side(style='thin'), right=Side(style='thin'),
            top=Side(style='thin'), bottom=Side(style='thin'),
        )

        for col_idx, (name, required, width, _) in enumerate(columns, 1):
            cell = ws.cell(row=1, column=col_idx, value=f'{name}（必填）' if required else name)
            cell.font = header_font
            cell.fill = required_fill if required else optional_fill
            cell.alignment = Alignment(horizontal='center')
            cell.border = thin_border
            ws.column_dimensions[openpyxl.utils.get_column_letter(col_idx)].width = width

        # 写入示例数据行
        for col_idx, (_, _, _, example) in enumerate(columns, 1):
            cell = ws.cell(row=2, column=col_idx, value=example)
            cell.border = thin_border

        # 数据验证：学历要求下拉
        edu_col = openpyxl.utils.get_column_letter(col_map_by_name['学历要求'])
        edu_dv = DataValidation(type='list', formula1='"博士,硕士,本科及以下"', allow_blank=False)
        edu_dv.error = '请选择：博士、硕士、本科及以下'
        edu_dv.errorTitle = '学历要求错误'
        ws.add_data_validation(edu_dv)
        edu_dv.add(f'{edu_col}2:{edu_col}500')

        # 数据验证：状态下拉
        status_col = openpyxl.utils.get_column_letter(col_map_by_name['状态'])
        status_dv = DataValidation(type='list', formula1='"草稿,已发布"', allow_blank=True)
        status_dv.error = '请选择：草稿、已发布'
        status_dv.errorTitle = '状态错误'
        ws.add_data_validation(status_dv)
        status_dv.add(f'{status_col}2:{status_col}500')

        # 数据验证：所属公司下拉（超管）
        if request.user.is_superadmin:
            org_names = list(Organization.objects.values_list('name', flat=True).order_by('name'))
            if org_names:
                # 将公司列表写入隐藏sheet，用名称引用实现下拉
                org_ws = wb.create_sheet('_org_data')
                for i, name in enumerate(org_names, 1):
                    org_ws.cell(row=i, column=1, value=name)
                org_ws.sheet_state = 'hidden'

                org_col = openpyxl.utils.get_column_letter(col_map_by_name['所属公司名称'])
                org_dv = DataValidation(
                    type='list',
                    formula1=f"'_org_data'!$A$1:$A${len(org_names)}",
                    allow_blank=False
                )
                org_dv.error = '请从下拉列表中选择公司，不可填写系统外的公司'
                org_dv.errorTitle = '公司名称错误'
                ws.add_data_validation(org_dv)
                org_dv.add(f'{org_col}2:{org_col}500')

        # 添加说明 sheet
        info_ws = wb.create_sheet('填写说明')
        info_ws.column_dimensions['A'].width = 20
        info_ws.column_dimensions['B'].width = 60
        notes = [
            ('字段', '说明'),
            ('职位名称', '必填，最多100个字符'),
            ('职位类别', '必填，如：技术、产品、设计、运营、市场等'),
            ('工作地点', '必填，如：北京、上海、深圳'),
            ('薪资范围', '必填，如：15k-25k、面议'),
            ('学历要求', '必填，可选值：博士、硕士、本科及以下'),
            ('招聘人数', '选填，默认为1，填写正整数'),
            ('职位描述', '选填，职位的详细描述'),
            ('状态', '选填，可选值：草稿、已发布，默认为草稿'),
        ]
        if request.user.is_superadmin:
            notes.insert(1, ('所属公司名称', '超管必填，填写系统中已有的公司名称'))
        for row_idx, (field, desc) in enumerate(notes, 1):
            a = info_ws.cell(row=row_idx, column=1, value=field)
            b = info_ws.cell(row=row_idx, column=2, value=desc)
            if row_idx == 1:
                a.font = Font(bold=True)
                b.font = Font(bold=True)

        buf = io.BytesIO()
        wb.save(buf)
        buf.seek(0)
        response = HttpResponse(
            buf.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        response['Content-Disposition'] = 'attachment; filename="job_import_template.xlsx"'
        return response

    @action(detail=False, methods=['post'], url_path='import-excel')
    def import_excel(self, request):
        """通过Excel批量导入职位"""
        file = request.FILES.get('file')
        if not file:
            return Response({'detail': '请上传Excel文件'}, status=status.HTTP_400_BAD_REQUEST)
        if not file.name.endswith(('.xlsx', '.xls')):
            return Response({'detail': '仅支持 .xlsx 或 .xls 格式'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            wb = openpyxl.load_workbook(file, read_only=True)
        except Exception:
            return Response({'detail': 'Excel文件解析失败，请检查文件格式'}, status=status.HTTP_400_BAD_REQUEST)

        ws = wb.active
        rows = list(ws.iter_rows(min_row=1, values_only=True))
        if len(rows) < 2:
            return Response({'detail': 'Excel中没有数据行'}, status=status.HTTP_400_BAD_REQUEST)

        # 解析表头，建立列名到索引的映射
        headers = [str(h).replace('（必填）', '').strip() if h else '' for h in rows[0]]
        col_map = {name: idx for idx, name in enumerate(headers)}

        user = request.user
        is_super = user.is_superadmin
        education_choices = {'博士', '硕士', '本科及以下'}
        status_choices = {'draft', 'published', '草稿', '已发布'}
        status_map = {'草稿': 'draft', '已发布': 'published', 'draft': 'draft', 'published': 'published'}

        # 超管需要提前加载公司名映射
        org_map = {}
        if is_super:
            org_map = {o.name: o for o in Organization.objects.all()}

        created = 0
        errors = []
        for row_idx, row in enumerate(rows[1:], start=2):
            def get_val(col_name):
                idx = col_map.get(col_name)
                if idx is not None and idx < len(row):
                    v = row[idx]
                    return str(v).strip() if v is not None else ''
                return ''

            title = get_val('职位名称')
            category = get_val('职位类别')
            location = get_val('工作地点')
            salary = get_val('薪资范围')
            education = get_val('学历要求')
            headcount_str = get_val('招聘人数')
            description = get_val('职位描述')
            job_status = get_val('状态')

            # 跳过全空行
            if not any([title, category, location, salary]):
                continue

            # 校验必填字段
            missing = []
            if not title:
                missing.append('职位名称')
            if not category:
                missing.append('职位类别')
            if not location:
                missing.append('工作地点')
            if not salary:
                missing.append('薪资范围')
            if not education:
                education = '本科及以下'

            if education not in education_choices:
                errors.append(f'第{row_idx}行：学历要求必须为 博士/硕士/本科及以下')
                continue

            if missing:
                errors.append(f'第{row_idx}行：缺少必填字段 {", ".join(missing)}')
                continue

            # 招聘人数
            headcount = 1
            if headcount_str:
                try:
                    headcount = int(float(headcount_str))
                    if headcount < 1:
                        raise ValueError
                except (ValueError, TypeError):
                    errors.append(f'第{row_idx}行：招聘人数必须为正整数')
                    continue

            # 状态：支持中文和英文
            job_status = status_map.get(job_status, 'draft')

            # 确定所属公司
            org = None
            if is_super:
                org_name = get_val('所属公司名称')
                if not org_name:
                    errors.append(f'第{row_idx}行：超级管理员必须填写所属公司名称')
                    continue
                org = org_map.get(org_name)
                if not org:
                    errors.append(f'第{row_idx}行：公司"{org_name}"不存在')
                    continue
            else:
                org = user.organization
                if not org:
                    return Response({'detail': '当前账号未关联公司，无法导入'}, status=status.HTTP_400_BAD_REQUEST)

            Job.objects.create(
                organization=org,
                title=title,
                category=category,
                location=location,
                salary=salary,
                education=education,
                headcount=headcount,
                description=description,
                status=job_status,
            )
            created += 1

        result = {'created': created, 'errors': errors}
        if created == 0 and errors:
            return Response(result, status=status.HTTP_400_BAD_REQUEST)
        return Response(result)


class DashboardView(APIView):
    """管理后台 Dashboard 统计数据"""
    permission_classes = [IsAdminOrSuperAdmin]

    def get(self, request):
        user = request.user
        is_super = user.is_superadmin

        # 根据角色过滤数据范围
        if is_super:
            jobs_qs = Job.objects.all()
            apps_qs = Application.objects.all()
        else:
            jobs_qs = Job.objects.filter(organization=user.organization)
            apps_qs = Application.objects.filter(job__organization=user.organization)

        # 职位统计
        job_stats = jobs_qs.aggregate(
            published=Count('id', filter=Q(status='published')),
            closed=Count('id', filter=Q(status='closed')),
            draft=Count('id', filter=Q(status='draft')),
            total=Count('id'),
        )

        # 投递统计
        app_stats = apps_qs.aggregate(
            total=Count('id'),
            pending=Count('id', filter=Q(status='pending')),
            viewed=Count('id', filter=Q(status='viewed')),
            interviewing=Count('id', filter=Q(status='interviewing')),
            hired=Count('id', filter=Q(status='hired')),
            rejected=Count('id', filter=Q(status='rejected')),
        )

        # 按投递人去重统计学历和院校分布（每人只算一次，取最高学历）
        DEGREE_RANK = {'博士': 4, 'MBA': 3, '硕士': 2, '本科': 1, '其他': 0}
        school_type_counter = Counter()
        degree_counter = Counter()

        def pick_top_edu(edu_list):
            """从教育经历中取最高学历的那条"""
            best = None
            best_rank = -1
            for edu in edu_list:
                d = edu.get('degree', '').strip()
                rank = DEGREE_RANK.get(d, 0)
                if rank > best_rank:
                    best_rank = rank
                    best = edu
            return best or (edu_list[0] if edu_list else None)

        # 按 applicant_id 去重，每个人只取一条投递快照
        seen_applicants = set()
        for applicant_id, snapshot in apps_qs.values_list('applicant_id', 'resume_snapshot'):
            if applicant_id in seen_applicants:
                continue
            seen_applicants.add(applicant_id)
            edu_list = snapshot.get('education', []) if snapshot else []
            edu_list = [e for e in edu_list if e.get('school', '').strip() or e.get('degree', '').strip()]
            top = pick_top_edu(edu_list)
            if top:
                degree = top.get('degree', '').strip()
                if degree:
                    degree_counter[degree] += 1
                school_name = top.get('school', '').strip()
                school_type_counter[classify_school(school_name)] += 1

        # 如果没有投递数据，从所有简历统计
        if not seen_applicants:
            from apps.resumes.models import Resume
            for resume in Resume.objects.all():
                edu_list = [e for e in (resume.education or []) if e.get('school', '').strip() or e.get('degree', '').strip()]
                top = pick_top_edu(edu_list)
                if top:
                    degree = top.get('degree', '').strip()
                    if degree:
                        degree_counter[degree] += 1
                    school_name = top.get('school', '').strip()
                    school_type_counter[classify_school(school_name)] += 1

        # 保证 985/211/其他 顺序固定
        school_type_dist = [
            {'name': '985', 'value': school_type_counter.get('985', 0)},
            {'name': '211', 'value': school_type_counter.get('211', 0)},
            {'name': '其他', 'value': school_type_counter.get('其他', 0)},
        ]
        degree_dist = [{'name': k, 'value': v} for k, v in degree_counter.items()]

        # 职位类别分布（top 10）
        category_dist = list(
            jobs_qs.values('category')
            .annotate(count=Count('id'))
            .order_by('-count')[:10]
        )

        return Response({
            'job_stats': job_stats,
            'app_stats': app_stats,
            'school_type_dist': school_type_dist,
            'degree_dist': degree_dist,
            'category_dist': [{'name': c['category'], 'value': c['count']} for c in category_dist],
        })
