"""
钉钉开放平台 API 服务
用于通过钉钉 API 获取组织架构和员工信息
"""

import httpx
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
from app.core.logger import get_logger

logger = get_logger(__name__)


class DingTalkService:
    """钉钉 API 服务"""
    
    BASE_URL = "https://api.dingtalk.com"
    OAPI_URL = "https://oapi.dingtalk.com"
    
    def __init__(
        self,
        corp_id: str,
        client_id: str,
        client_secret: str
    ):
        """
        初始化钉钉服务
        
        Args:
            corp_id: 企业 CorpId
            client_id: 应用 ClientId (AppKey)
            client_secret: 应用 ClientSecret (AppSecret)
        """
        self.corp_id = corp_id
        self.client_id = client_id
        self.client_secret = client_secret
        self._access_token: Optional[str] = None
        self._token_expires_at: Optional[datetime] = None
    
    async def get_access_token(self) -> str:
        """
        获取钉钉 Access Token
        
        使用新版 OAuth2 接口获取
        
        Returns:
            access_token
        """
        # 检查缓存的 token 是否有效
        if self._access_token and self._token_expires_at:
            if datetime.now() < self._token_expires_at - timedelta(minutes=5):
                return self._access_token
        
        url = f"{self.BASE_URL}/v1.0/oauth2/{self.corp_id}/token"
        
        payload = {
            "client_id": self.client_id,
            "client_secret": self.client_secret,
            "grant_type": "client_credentials"
        }
        
        async with httpx.AsyncClient(timeout=30) as client:
            response = await client.post(url, json=payload)
            response.raise_for_status()
            data = response.json()
            
            self._access_token = data["access_token"]
            expires_in = data.get("expires_in", 7200)
            self._token_expires_at = datetime.now() + timedelta(seconds=expires_in)
            
            logger.info(f"获取钉钉 Access Token 成功，有效期 {expires_in} 秒")
            return self._access_token
    
    async def get_department_list(self, dept_id: int = 1) -> List[Dict[str, Any]]:
        """
        获取部门列表
        
        Args:
            dept_id: 父部门ID，根部门为1
            
        Returns:
            部门列表
        """
        access_token = await self.get_access_token()
        url = f"{self.OAPI_URL}/topapi/v2/department/listsub"
        
        params = {"access_token": access_token}
        payload = {"dept_id": dept_id}
        
        async with httpx.AsyncClient(timeout=30) as client:
            response = await client.post(url, params=params, json=payload)
            response.raise_for_status()
            data = response.json()
            
            if data.get("errcode") != 0:
                raise Exception(f"获取部门列表失败: {data.get('errmsg')}")
            
            return data.get("result", [])
    
    async def get_all_departments(self) -> List[Dict[str, Any]]:
        """
        递归获取所有部门
        
        Returns:
            所有部门列表（扁平化）
        """
        all_departments = []
        
        async def fetch_recursive(parent_id: int):
            departments = await self.get_department_list(parent_id)
            for dept in departments:
                all_departments.append(dept)
                # 递归获取子部门
                await fetch_recursive(dept["dept_id"])
        
        await fetch_recursive(1)  # 从根部门开始
        logger.info(f"获取到 {len(all_departments)} 个部门")
        return all_departments
    
    async def get_department_users(
        self, 
        dept_id: int, 
        cursor: int = 0, 
        size: int = 100
    ) -> Dict[str, Any]:
        """
        获取部门用户列表
        
        Args:
            dept_id: 部门ID
            cursor: 分页游标
            size: 每页大小，最大100
            
        Returns:
            用户列表和分页信息
        """
        access_token = await self.get_access_token()
        url = f"{self.OAPI_URL}/topapi/v2/user/list"
        
        params = {"access_token": access_token}
        payload = {
            "dept_id": dept_id,
            "cursor": cursor,
            "size": size
        }
        
        async with httpx.AsyncClient(timeout=30) as client:
            response = await client.post(url, params=params, json=payload)
            response.raise_for_status()
            data = response.json()
            
            if data.get("errcode") != 0:
                raise Exception(f"获取部门用户失败: {data.get('errmsg')}")
            
            return data.get("result", {})
    
    async def get_all_employees(self) -> List[Dict[str, Any]]:
        """
        获取所有在职员工
        
        遍历所有部门获取员工列表
        
        Returns:
            员工列表
        """
        logger.info("开始从钉钉 API 获取所有员工...")
        
        # 1. 获取所有部门
        departments = await self.get_all_departments()
        
        # 创建部门ID到名称的映射
        dept_map = {dept["dept_id"]: dept["name"] for dept in departments}
        dept_map[1] = "根部门"  # 添加根部门
        
        # 2. 遍历所有部门获取员工
        all_employees = {}  # 使用字典去重（按 userid）
        
        for dept in [{"dept_id": 1, "name": "根部门"}] + departments:
            dept_id = dept["dept_id"]
            dept_name = dept["name"]
            
            cursor = 0
            while True:
                result = await self.get_department_users(dept_id, cursor)
                users = result.get("list", [])
                
                for user in users:
                    userid = user.get("userid")
                    if userid and userid not in all_employees:
                        # 转换为统一格式
                        employee = self._convert_user_to_employee(user, dept_name)
                        all_employees[userid] = employee
                
                # 检查是否还有更多数据
                if not result.get("has_more", False):
                    break
                cursor = result.get("next_cursor", 0)
        
        employees = list(all_employees.values())
        logger.info(f"获取到 {len(employees)} 位在职员工")
        return employees
    
    def _convert_user_to_employee(
        self, 
        user: Dict[str, Any], 
        dept_name: str
    ) -> Dict[str, Any]:
        """
        将钉钉用户数据转换为员工数据格式
        
        Args:
            user: 钉钉用户数据
            dept_name: 部门名称
            
        Returns:
            标准员工数据格式
        """
        return {
            'full_name': user.get('name', ''),
            'phone': user.get('mobile', ''),
            'email': user.get('email', ''),
            'department': dept_name,
            'position': user.get('title', ''),
            'employee_no': user.get('job_number', ''),
            'is_leader': user.get('leader', False),
            'is_active': user.get('active', True),
            'dingtalk_id': user.get('userid', ''),
            'join_date': user.get('hired_date'),
            'work_location': user.get('work_place', ''),
            'avatar': user.get('avatar', ''),
        }
    
    async def test_connection(self) -> Dict[str, Any]:
        """
        测试钉钉 API 连接
        
        Returns:
            测试结果
        """
        try:
            # 1. 测试获取 token
            token = await self.get_access_token()
            
            # 2. 测试获取根部门信息
            departments = await self.get_department_list(1)
            
            # 3. 获取根部门员工数量
            result = await self.get_department_users(1, size=1)
            
            return {
                "success": True,
                "message": "连接成功",
                "corp_id": self.corp_id,
                "department_count": len(departments) + 1,  # +1 是根部门
                "has_employees": result.get("has_more", False) or len(result.get("list", [])) > 0
            }
            
        except httpx.HTTPStatusError as e:
            error_detail = "HTTP错误"
            if e.response.status_code == 400:
                try:
                    error_data = e.response.json()
                    error_detail = error_data.get("message", str(e))
                except:
                    pass
            return {
                "success": False,
                "message": f"连接失败: {error_detail}",
                "error": str(e)
            }
        except Exception as e:
            return {
                "success": False,
                "message": f"连接失败: {str(e)}",
                "error": str(e)
            }
