"""
国自然平台直接爬取模块 - 使用 Playwright 直接操作
"""

import asyncio
import random
import re
from typing import List, Optional, Dict, Any, Tuple
from urllib.parse import quote
from datetime import datetime

from playwright.async_api import async_playwright, Page, Browser

from .models import ProjectInfo, SearchResults


class NSFCDirectScraper:
    """国自然平台直接爬取器"""
    
    BASE_URL = "https://kd.nsfc.cn"
    
    # 延迟配置（秒）- 防止反爬
    DELAY_PAGE_LOAD = (2.0, 3.5)      # 页面加载后等待
    DELAY_BETWEEN_PAGES = (1.5, 3.0)  # 翻页间隔
    DELAY_DETAIL_REQUEST = (2.0, 4.0) # 详情页请求间隔
    DELAY_CLICK = (1.0, 2.0)          # 点击操作间隔
    
    def __init__(self, headless: bool = False):
        self.headless = headless
        self._playwright = None
        self._browser: Optional[Browser] = None
        self._page: Optional[Page] = None
    
    async def _random_delay(self, delay_range: tuple):
        """随机延迟，模拟人类行为"""
        delay = random.uniform(delay_range[0], delay_range[1])
        await asyncio.sleep(delay)
    
    async def _search_by_years(self, keyword: str, pages: int, years: List[int]) -> SearchResults:
        """按年份分批搜索，获取更全面的结果
        
        通过普通搜索页面的左侧年份筛选面板，逐年搜索指定的结题年份
        """
        if not self._page:
            raise RuntimeError("浏览器未启动")
        
        all_projects: List[dict] = []
        max_retries = 3  # 最大重试次数
        
        for year in years:
            print(f"\n📅 搜索 {year} 年结题项目: {keyword}")
            
            year_projects = []
            success = False
            
            for retry in range(max_retries):
                if retry > 0:
                    print(f"    🔄 重试 {retry}/{max_retries-1}...")
                
                try:
                    # 访问搜索页面
                    search_url = f"{self.BASE_URL}/finalSearchList?s={quote(keyword)}"
                    await self._page.goto(search_url)
                    await self._random_delay(self.DELAY_PAGE_LOAD)
                    
                    # 点击检索按钮触发搜索
                    await self._page.click('button:has-text("检索")')
                    await self._random_delay(self.DELAY_PAGE_LOAD)
                    
                    # 等待年份筛选面板加载（等待 radio 出现）
                    # 使用 role-based selector，因为年份是 radio button
                    year_selector = f'role=radio[name="{year}"]'
                    try:
                        await self._page.wait_for_selector(year_selector, timeout=10000)
                    except Exception:
                        print(f"    ⚠ 年份 {year} 筛选项未出现，可能该年份无数据")
                        break
                    
                    # 点击年份筛选
                    await self._page.click(year_selector)
                    await self._random_delay(self.DELAY_PAGE_LOAD)
                    
                    # 等待结果更新（检查结果数量变化）
                    await self._page.wait_for_function(
                        'document.body.innerText.includes("根据条件检索到")',
                        timeout=10000
                    )
                    await self._random_delay((1.0, 2.0))
                    
                    # 获取该年份的总页数
                    total_pages = await self._get_total_pages()
                    pages_to_fetch = total_pages if pages == 0 else min(pages, total_pages)
                    print(f"    {year}年: 共 {total_pages} 页，将获取 {pages_to_fetch} 页")
                    
                    # 如果没有结果，跳过
                    if total_pages == 0:
                        print(f"    ⚠ {year}年无结果")
                        success = True
                        break
                    
                    # 提取该年份的项目
                    for page_num in range(1, pages_to_fetch + 1):
                        if page_num > 1:
                            await self._goto_page(page_num)
                            await self._random_delay(self.DELAY_BETWEEN_PAGES)
                        
                        projects = await self._extract_list_page()
                        year_projects.extend(projects)
                        print(f"    第 {page_num}/{pages_to_fetch} 页: 获取 {len(projects)} 条")
                    
                    success = True
                    break
                    
                except Exception as e:
                    print(f"    ⚠ 搜索失败: {e}")
                    if retry < max_retries - 1:
                        await self._random_delay((2.0, 4.0))
            
            if success and year_projects:
                all_projects.extend(year_projects)
                print(f"    ✓ {year}年共获取 {len(year_projects)} 条")
            elif not success:
                print(f"    ❌ {year}年搜索失败，已跳过")
        
        # 去重（按批准号）
        seen = set()
        unique_projects = []
        for proj in all_projects:
            key = proj.get('approval_no') or proj.get('title')
            if key and key not in seen:
                seen.add(key)
                unique_projects.append(proj)
        
        print(f"\n📊 总计: {len(all_projects)} 条，去重后: {len(unique_projects)} 条")
        
        # 获取详情
        print(f"\n📄 获取详情页信息 (共 {len(unique_projects)} 个)...")
        for i, proj in enumerate(unique_projects):
            if proj.get('detail_id'):
                title = proj.get('title', '')[:30]
                print(f"  [{i+1}/{len(unique_projects)}] {title}...")
                detail = await self._fetch_detail(proj['detail_id'])
                proj.update(detail)
                await self._random_delay(self.DELAY_DETAIL_REQUEST)
        
        # 转换为 ProjectInfo
        project_list = self._convert_to_project_info(unique_projects)
        
        years_str = ",".join(str(y) for y in years)
        return SearchResults(
            keyword=f"{keyword}[{years_str}]",
            total_count=len(project_list),
            projects=project_list
        )
    
    async def start(self):
        """启动浏览器"""
        self._playwright = await async_playwright().start()
        self._browser = await self._playwright.chromium.launch(headless=self.headless)
        self._page = await self._browser.new_page()
    
    async def close(self):
        """关闭浏览器"""
        if self._browser:
            await self._browser.close()
        if self._playwright:
            await self._playwright.stop()
    
    async def search(self, keyword: str, pages: int = 1, years: Optional[List[int]] = None) -> SearchResults:
        """搜索国自然项目并获取详情
        
        Args:
            keyword: 搜索关键词
            pages: 每个年份爬取的页数，0表示全部
            years: 指定结题年份列表，如 [2024, 2023, 2022]，None 表示使用默认搜索（近5年）
        """
        print("🚀 启动浏览器...")
        if not self._page:
            await self.start()
        
        if not self._page:
            raise RuntimeError("浏览器启动失败")
        
        # 如果指定了年份，使用按年份分批爬取
        if years:
            return await self._search_by_years(keyword, pages, years)
        
        # 1. 访问搜索页面（默认模式：近5年）
        print(f"🔍 访问搜索页面: {keyword}")
        search_url = f"{self.BASE_URL}/finalSearchList?s={quote(keyword)}"
        await self._page.goto(search_url)
        await self._random_delay(self.DELAY_PAGE_LOAD)
        
        # 2. 点击检索按钮触发搜索
        print("📋 触发搜索...")
        try:
            await self._page.click('button:has-text("检索")')
            await self._random_delay(self.DELAY_PAGE_LOAD)
        except Exception:
            pass
        
        # 3. 获取总页数
        total_pages = await self._get_total_pages()
        # pages=0 表示获取全部页面
        if pages == 0:
            pages_to_fetch = total_pages
        else:
            pages_to_fetch = min(pages, total_pages)
        print(f"共 {total_pages} 页，将获取 {pages_to_fetch} 页")
        
        # 4. 提取所有项目的基本信息和详情页ID
        all_projects = []
        
        for page_num in range(1, pages_to_fetch + 1):
            if page_num > 1:
                await self._goto_page(page_num)
                await self._random_delay(self.DELAY_BETWEEN_PAGES)
            
            projects = await self._extract_list_page()
            all_projects.extend(projects)
            print(f"第 {page_num} 页: 获取 {len(projects)} 条")
        
        # 5. 获取每个项目的详情
        print(f"\n📄 获取详情页信息 (共 {len(all_projects)} 个)...")
        for i, proj in enumerate(all_projects):
            if proj.get('detail_id'):
                print(f"  [{i+1}/{len(all_projects)}] {proj['title'][:30]}...")
                detail = await self._fetch_detail(proj['detail_id'])
                proj.update(detail)
                await self._random_delay(self.DELAY_DETAIL_REQUEST)  # 关键：详情页间隔
        
        # 6. 转换为 ProjectInfo
        project_list = self._convert_to_project_info(all_projects)
        
        return SearchResults(
            keyword=keyword,
            total_count=len(project_list),
            projects=project_list
        )
    
    async def _get_total_pages(self) -> int:
        """获取总页数"""
        if not self._page:
            return 1
        try:
            text = await self._page.inner_text('body')
            match = re.search(r'共(\d+)页', text)
            if match:
                return int(match.group(1))
        except Exception:
            pass
        return 1
    
    async def _goto_page(self, page_num: int):
        """跳转到指定页"""
        if not self._page:
            return
        try:
            await self._page.fill('input[type="number"]', str(page_num))
            await self._page.keyboard.press('Enter')
            await self._random_delay(self.DELAY_PAGE_LOAD)
        except Exception:
            pass
    
    async def _extract_list_page(self) -> List[dict]:
        """从列表页提取项目基本信息"""
        if not self._page:
            return []
        
        print("    📋 提取列表页数据...")
        projects = []
        
        # 使用 JavaScript 提取项目信息
        data = await self._page.evaluate('''() => {
            const items = [];
            // 查找所有项目卡片
            const cards = document.querySelectorAll('[class*="project"], [class*="item"]');
            
            // 备用方案：通过文本内容查找
            const body = document.body.innerText;
            const regex = /(\\d+)\\.([^\\n]+)\\n+批准号：([^\\n]+)\\n申请代码：([^\\n]+)\\n项目类别：\\s*([^\\n]+)\\n项目负责人：\\s*([^\\n]+)\\n资助经费：([^\\n]+)\\n批准年度：(\\d+)\\n结题年度：(\\d+)\\n依托单位：\\s*([^\\n]+)\\n关键词：\\s*([^\\n]+)/gm;
            
            let match;
            while ((match = regex.exec(body)) !== null) {
                items.push({
                    index: match[1],
                    title: match[2].trim(),
                    approval_no: match[3].trim(),
                    code: match[4].trim(),
                    category: match[5].trim(),
                    leader: match[6].trim(),
                    funding: match[7].trim(),
                    approval_year: match[8],
                    end_year: match[9],
                    institution: match[10].trim(),
                    keywords: match[11].trim()
                });
            }
            
            // 提取详情页链接ID
            const links = document.querySelectorAll('a[href*="finalDetails"]');
            const detailIds = [];
            links.forEach(link => {
                const href = link.getAttribute('href');
                const idMatch = href.match(/id=([a-f0-9]+)/);
                if (idMatch) {
                    detailIds.push(idMatch[1]);
                }
            });
            
            // 也尝试从点击事件中获取
            const paragraphs = document.querySelectorAll('p[class*="title"], p');
            paragraphs.forEach((p, idx) => {
                const text = p.innerText;
                if (text.match(/^\\d+\\./)) {
                    // 这是项目标题
                }
            });
            
            return { items, detailIds };
        }''')
        
        items = data.get('items', [])
        detail_ids = data.get('detailIds', [])
        print(f"    ✓ JS提取: {len(items)} 条, {len(detail_ids)} 个详情ID")
        
        # 如果没有通过正则提取到，尝试其他方式
        if not items:
            print("    🔄 JS提取失败，尝试文本提取...")
            items = await self._extract_by_text()
            print(f"    ✓ 文本提取: {len(items)} 条")
        
        # 尝试获取详情页ID
        if not detail_ids and items:
            print(f"    🔄 尝试从HTML提取详情ID...")
            detail_ids = await self._get_detail_ids_from_html()
        
        # HTML提取失败，用点击方式
        if not detail_ids and items:
            print(f"    🔄 通过点击获取详情ID ({len(items)} 个)...")
            detail_ids = await self._get_detail_ids_by_click(len(items))
        
        # 合并详情ID
        for i, item in enumerate(items):
            if i < len(detail_ids):
                item['detail_id'] = detail_ids[i]
            else:
                item['detail_id'] = None
        
        return items
    
    async def _extract_by_text(self) -> List[dict]:
        """通过文本提取项目信息"""
        if not self._page:
            return []
        
        text = await self._page.inner_text('body')
        projects = []
        
        regex = re.compile(
            r'(\d+)\.([^\n]+)\n+'
            r'批准号：([^\n]+)\n'
            r'申请代码：([^\n]+)\n'
            r'项目类别：\s*([^\n]+)\n'
            r'项目负责人：\s*([^\n]+)\n'
            r'资助经费：([^\n]+)\n'
            r'批准年度：(\d+)\n'
            r'结题年度：(\d+)\n'
            r'依托单位：\s*([^\n]+)\n'
            r'关键词：\s*([^\n]+)',
            re.MULTILINE
        )
        
        for match in regex.finditer(text):
            projects.append({
                'index': match.group(1),
                'title': match.group(2).strip(),
                'approval_no': match.group(3).strip(),
                'code': match.group(4).strip(),
                'category': match.group(5).strip(),
                'leader': match.group(6).strip(),
                'funding': match.group(7).strip(),
                'approval_year': match.group(8),
                'end_year': match.group(9),
                'institution': match.group(10).strip(),
                'keywords': match.group(11).strip(),
                'detail_id': None
            })
        
        return projects
    
    async def _get_detail_ids_from_html(self) -> List[str]:
        """从HTML中提取详情页ID（快速方式）"""
        if not self._page:
            return []
        
        try:
            # 尝试多种方式获取详情ID
            ids = await self._page.evaluate('''() => {
                const ids = [];
                
                // 方式1: 从onclick属性获取
                document.querySelectorAll('[onclick*="finalDetails"]').forEach(el => {
                    const match = el.getAttribute('onclick').match(/id=([a-f0-9]+)/);
                    if (match) ids.push(match[1]);
                });
                
                // 方式2: 从href获取
                if (ids.length === 0) {
                    document.querySelectorAll('a[href*="finalDetails"]').forEach(el => {
                        const match = el.href.match(/id=([a-f0-9]+)/);
                        if (match) ids.push(match[1]);
                    });
                }
                
                // 方式3: 从data属性获取
                if (ids.length === 0) {
                    document.querySelectorAll('[data-id]').forEach(el => {
                        ids.push(el.getAttribute('data-id'));
                    });
                }
                
                return ids;
            }''')
            
            print(f"    ✓ HTML提取: {len(ids)} 个详情ID")
            return ids
        except Exception as e:
            print(f"    ⚠ HTML提取失败: {e}")
            return []
    
    async def _get_detail_ids_by_click(self, count: int) -> List[str]:
        """通过监听新标签页获取详情ID - 只点击项目标题"""
        if not self._page or not self._browser:
            return []
        
        detail_ids = []
        ctx = self._browser.contexts[0]
        
        # 获取所有项目卡片，每个卡片只取第一个 a 标签（项目标题）
        cards = await self._page.query_selector_all('.info-warp')
        title_links = []
        for card in cards:
            first_a = await card.query_selector('a')
            if first_a:
                title_links.append(first_a)
        
        print(f"    找到 {len(title_links)} 个项目标题链接")
        
        # 点击每个标题，通过事件监听获取新页面
        for i, a in enumerate(title_links[:count]):
            try:
                print(f"      [{i+1}/{count}] 获取详情ID...", end="\r")
                
                # 用列表收集新页面
                new_pages = []
                def on_page(page):
                    new_pages.append(page)
                
                ctx.on('page', on_page)
                await a.click()
                await self._random_delay(self.DELAY_CLICK)
                ctx.remove_listener('page', on_page)
                
                if new_pages:
                    new_page = new_pages[0]
                    url = new_page.url
                    id_match = re.search(r'id=([a-f0-9]+)', url)
                    if id_match:
                        detail_ids.append(id_match.group(1))
                    else:
                        detail_ids.append(None)
                    await new_page.close()
                else:
                    detail_ids.append(None)
                    
            except Exception as e:
                detail_ids.append(None)
        
        print(f"    ✓ 获取 {len([x for x in detail_ids if x])} 个详情ID")
        return detail_ids
    
    async def _extract_publications_from_page(self, page) -> List[dict]:
        """从当前页面提取成果产出列表"""
        publications = []
        
        try:
            body = await page.inner_text('body')
            
            # 查找成果产出区域
            output_match = re.search(r'成果产出([\s\S]*?)(?=科研影响分析|$)', body)
            if not output_match:
                return publications
                
            output_text = output_match.group(1)
            
            # 实际页面格式: 
            # "1.标题\n\n类型：期刊论文作者：xxx\n\n2.标题..."
            # 类型和作者之间没有换行，是连在一起的
            
            # 方法1: 使用更灵活的正则表达式
            pattern = re.compile(
                r'(\d+)\.([^\n]+)\n+'           # 序号.标题
                r'类型[：:]\s*([^作]+)'          # 类型：xxx (匹配到"作"字之前)
                r'作者[：:]\s*([^\n\d]+)',       # 作者：xxx (匹配到换行或下一个数字之前)
                re.MULTILINE
            )
            
            for match in pattern.finditer(output_text):
                title = match.group(2).strip()
                pub_type = match.group(3).strip()
                authors = match.group(4).strip()
                
                # 清理可能的尾随空格和页码
                authors = re.sub(r'\s*\d+\s*$', '', authors)
                
                if title:  # 只添加有标题的
                    publications.append({
                        'title': title,
                        'pub_type': pub_type,
                        'authors': authors
                    })
            
            # 如果方法1没提取到，尝试方法2: 更宽松的匹配
            if not publications:
                # 分割每个成果条目
                entries = re.split(r'(?=\d+\.)', output_text)
                for entry in entries:
                    entry = entry.strip()
                    if not entry or not re.match(r'^\d+\.', entry):
                        continue
                    
                    # 提取标题 (第一行)
                    lines = entry.split('\n')
                    title_match = re.match(r'^\d+\.(.+)$', lines[0])
                    if not title_match:
                        continue
                    
                    title = title_match.group(1).strip()
                    
                    # 在剩余文本中查找类型和作者
                    rest_text = '\n'.join(lines[1:])
                    type_match = re.search(r'类型[：:]\s*([^作\n]+)', rest_text)
                    author_match = re.search(r'作者[：:]\s*([^\n]+)', rest_text)
                    
                    if title:
                        publications.append({
                            'title': title,
                            'pub_type': type_match.group(1).strip() if type_match else None,
                            'authors': author_match.group(1).strip() if author_match else None
                        })
                        
        except Exception as e:
            print(f"      ⚠ 提取成果产出失败: {e}")
        
        return publications
    
    async def _get_publications_total_pages(self, page) -> int:
        """获取成果产出的总页数"""
        try:
            # 查找成果产出区域的分页信息
            # 分页格式通常是 "共X页" 或者页码列表
            body = await page.inner_text('body')
            
            # 查找成果产出区域后的分页
            output_section = re.search(r'成果产出([\s\S]*?)(?=科研影响分析|$)', body)
            if output_section:
                section_text = output_section.group(1)
                # 查找 "前往 X 页" 中的最大页码
                page_nums = re.findall(r'"(\d+)"', section_text)
                if page_nums:
                    return max(int(p) for p in page_nums)
            
            # 备用：通过 JavaScript 查找分页控件
            pages = await page.evaluate('''() => {
                // 查找成果产出区域内的分页
                const paginationItems = document.querySelectorAll('#project_results li, [class*="pagination"] li');
                let maxPage = 1;
                paginationItems.forEach(item => {
                    const text = item.innerText.trim();
                    const num = parseInt(text);
                    if (!isNaN(num) && num > maxPage) {
                        maxPage = num;
                    }
                });
                return maxPage;
            }''')
            
            return pages if pages else 1
            
        except Exception:
            return 1
    
    async def _goto_publications_page(self, page, page_num: int):
        """跳转到成果产出的指定页"""
        try:
            # 方法1: 点击页码
            # 成果产出分页在 #project_results 区域内
            page_selector = f'#project_results li:has-text("{page_num}")'
            try:
                await page.click(page_selector, timeout=3000)
                await self._random_delay((1.0, 2.0))
                return
            except Exception:
                pass
            
            # 方法2: 使用输入框跳转
            try:
                # 找到成果产出区域的页码输入框
                spinbuttons = await page.query_selector_all('#project_results input[type="number"], #project_results spinbutton')
                if spinbuttons:
                    await spinbuttons[0].fill(str(page_num))
                    await page.keyboard.press('Enter')
                    await self._random_delay((1.0, 2.0))
            except Exception:
                pass
                
        except Exception as e:
            print(f"      ⚠ 跳转成果页失败: {e}")
    
    async def _fetch_detail(self, detail_id: str) -> dict:
        """获取项目详情页信息（包括成果产出）"""
        if not self._browser:
            return {}
        
        detail_url = f"{self.BASE_URL}/finalDetails?id={detail_id}"
        
        try:
            new_page = await self._browser.new_page()
            await new_page.goto(detail_url)
            await self._random_delay(self.DELAY_PAGE_LOAD)  # 等待页面加载
            
            # 获取页面文本用于基本信息提取
            body = await new_page.inner_text('body')
            
            detail: Dict[str, Any] = {'detail_url': detail_url}
            
            # 研究期限
            period_match = re.search(r'研究期限[：:]\s*([\d-]+\s*至\s*[\d-]+)', body)
            if period_match:
                detail['research_period'] = period_match.group(1).strip()
            
            # 中文摘要
            cn_match = re.search(r'中文摘要[：:]\s*([\s\S]*?)(?=英文摘要[：:]|$)', body)
            if cn_match:
                detail['abstract_cn'] = cn_match.group(1).strip()
            
            # 英文摘要
            en_match = re.search(r'英文摘要[：:]\s*([\s\S]*?)(?=结题摘要|$)', body)
            if en_match:
                detail['abstract_en'] = en_match.group(1).strip()
            
            # 结题摘要
            concl_match = re.search(r'结题摘要\s*([\s\S]*?)(?=结题报告|$)', body)
            if concl_match:
                text = concl_match.group(1).strip()
                if text and text != '暂无数据' and text != '暂无结题摘要':
                    detail['conclusion_abstract'] = text
            
            # 成果统计 - 格式为 "期刊论文数量\n9" (数量在下一行)
            paper_match = re.search(r'期刊论文数量\s*\n?\s*(\d+)', body)
            if paper_match:
                detail['paper_count'] = int(paper_match.group(1))
            
            conf_paper_match = re.search(r'会议论文数量\s*\n?\s*(\d+)', body)
            if conf_paper_match:
                detail['conference_paper_count'] = int(conf_paper_match.group(1))
            
            patent_match = re.search(r'专利数量\s*\n?\s*(\d+)', body)
            if patent_match:
                detail['patent_count'] = int(patent_match.group(1))
            
            book_match = re.search(r'专著数量\s*\n?\s*(\d+)', body)
            if book_match:
                detail['book_count'] = int(book_match.group(1))
            
            award_match = re.search(r'科研奖励数量\s*\n?\s*(\d+)', body)
            if award_match:
                detail['award_count'] = int(award_match.group(1))
            
            # 提取成果产出（带分页处理）
            all_publications = []
            
            # 检查是否有成果产出
            if '抱歉，无成果产出' not in body:
                # 获取第一页的成果
                first_page_pubs = await self._extract_publications_from_page(new_page)
                all_publications.extend(first_page_pubs)
                
                # 检查是否有更多页
                total_pub_pages = await self._get_publications_total_pages(new_page)
                
                if total_pub_pages > 1:
                    print(f"      📄 成果产出共 {total_pub_pages} 页")
                    for pub_page_num in range(2, total_pub_pages + 1):
                        await self._goto_publications_page(new_page, pub_page_num)
                        page_pubs = await self._extract_publications_from_page(new_page)
                        all_publications.extend(page_pubs)
                        print(f"        第 {pub_page_num}/{total_pub_pages} 页: {len(page_pubs)} 条")
            
            if all_publications:
                detail['publications'] = all_publications
                print(f"      ✓ 获取 {len(all_publications)} 条成果产出")
            
            await new_page.close()
            return detail
            
        except Exception as e:
            print(f"    ⚠ 详情获取失败: {e}")
            return {'detail_url': detail_url}
    
    def _convert_to_project_info(self, projects: List[dict]) -> List[ProjectInfo]:
        """转换为 ProjectInfo 列表"""
        from .models import Publication
        
        result = []
        for proj in projects:
            # 转换成果产出
            publications = None
            if proj.get('publications'):
                publications = [
                    Publication(
                        title=pub.get('title', ''),
                        pub_type=pub.get('pub_type'),
                        authors=pub.get('authors'),
                        journal=pub.get('journal'),
                        year=pub.get('year')
                    )
                    for pub in proj.get('publications', [])
                ]
            
            result.append(ProjectInfo(
                project_name=proj.get('title', ''),
                project_leader=proj.get('leader', ''),
                institution=proj.get('institution', ''),
                approval_no=proj.get('approval_no'),
                apply_code=proj.get('code'),
                project_type=proj.get('category'),
                funding_amount=proj.get('funding'),
                approval_year=proj.get('approval_year'),
                end_year=proj.get('end_year'),
                keywords=proj.get('keywords'),
                detail_url=proj.get('detail_url'),
                research_period=proj.get('research_period'),
                abstract_cn=proj.get('abstract_cn'),
                abstract_en=proj.get('abstract_en'),
                conclusion_abstract=proj.get('conclusion_abstract'),
                paper_count=proj.get('paper_count'),
                conference_paper_count=proj.get('conference_paper_count'),
                patent_count=proj.get('patent_count'),
                book_count=proj.get('book_count'),
                award_count=proj.get('award_count'),
                publications=publications,
            ))
        return result
