# src/rules/static/relative_link.py
import re
import os
from typing import List, Dict, Any, Optional
from pathlib import Path
from ..base import BaseRule, Diagnostic


class RelativeLinkRule(BaseRule):
    """检测 Markdown 相对路径引用是否正确"""

    rule_id = "relative-link-check"
    severity = "warning"

    # Markdown 链接正则：[text](url) 或 [text](url "title")
    LINK_PATTERN = re.compile(r'\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)')
    # Markdown 图片正则：![alt](url)
    IMAGE_PATTERN = re.compile(r'!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)')
    # 内部锚点正则：#heading
    ANCHOR_PATTERN = re.compile(r'^#')
    # 外部链接正则：http://或 https://
    EXTERNAL_PATTERN = re.compile(r'^https?://')
    # 邮件链接正则：mailto:
    EMAIL_PATTERN = re.compile(r'^mailto:')

    def __init__(self, config: Optional[Dict[str, Any]] = None):
        super().__init__(config)
        self.check_images = self.config.get('check_images', True)
        self.allowed_extensions = self.config.get('allowed_extensions',
                                                  ['.md', '.png', '.jpg', '.jpeg', '.gif', '.svg'])
        self.base_dir = self.config.get('base_dir', None)

    def check(self, file_path: str, content: str) -> List[Diagnostic]:
        diagnostics = []
        file_dir = os.path.dirname(os.path.abspath(file_path))

        # 检查普通链接
        diagnostics.extend(self._check_links(content, file_path, file_dir, is_image=False))

        # 检查图片链接
        if self.check_images:
            diagnostics.extend(self._check_links(content, file_path, file_dir, is_image=True))

        return diagnostics

    def _check_links(self, content: str, file_path: str, file_dir: str, is_image: bool) -> List[Diagnostic]:
        diagnostics = []
        pattern = self.IMAGE_PATTERN if is_image else self.LINK_PATTERN
        link_type = "图片" if is_image else "链接"

        lines = content.split('\n')
        line_offsets = [0]
        for line in lines:
            line_offsets.append(line_offsets[-1] + len(line) + 1)

        for match in pattern.finditer(content):
            url = match.group(2)

            # 跳过外部链接、锚点、邮件链接
            if self.EXTERNAL_PATTERN.match(url) or self.ANCHOR_PATTERN.match(url) or self.EMAIL_PATTERN.match(url):
                continue

            # 跳过协议链接
            if ':' in url.split('/')[0]:
                continue

            # 计算行号
            pos = match.start()
            line_num = 1
            for i, offset in enumerate(line_offsets):
                if offset > pos:
                    line_num = i
                    break

            # 解析相对路径
            target_path = self._resolve_relative_path(url, file_dir)

            if target_path and not os.path.exists(target_path):
                diagnostics.append(Diagnostic(
                    file=file_path,
                    line=line_num,
                    column=match.start() - line_offsets[line_num - 1] + 1,
                    message=f"{link_type}指向的文件不存在：{url}",
                    severity=self.severity,
                    rule_id=self.rule_id
                ))

        return diagnostics

    def _resolve_relative_path(self, url: str, base_dir: str) -> Optional[str]:
        """解析相对路径为绝对路径"""
        # 移除锚点部分
        url = url.split('#')[0]

        if not url:
            return None

        try:
            # 处理相对路径
            if url.startswith('./'):
                url = url[2:]
            elif url.startswith('../'):
                pass  # 保持原样，join 会处理

            target = os.path.normpath(os.path.join(base_dir, url))
            return target
        except Exception:
            return None