/**
 * 自定义 Vite Mock 插件
 * 使用 configureServer 中间件拦截请求，不依赖任何第三方库
 * 与所有 Vite 版本兼容
 */
import type { Plugin, ViteDevServer } from 'vite'
import type { MockMethod, MockRequest } from './types'
import fs from 'fs'
import path from 'path'

interface MockPluginOptions {
  mockPath: string
  enable?: boolean
}

export function viteMockPlugin(options: MockPluginOptions): Plugin {
  const { mockPath, enable = true } = options

  return {
    name: 'vite-mock-plugin',
    configureServer(server: ViteDevServer) {
      if (!enable) return

      // 缓存已加载的 mock 路由
      let mockRoutes: MockMethod[] = []
      let loaded = false

      async function loadMockRoutes(): Promise<MockMethod[]> {
        if (loaded) return mockRoutes
        const mockDir = path.resolve(process.cwd(), mockPath)
        if (!fs.existsSync(mockDir)) {
          console.warn(`[mock] 目录不存在: ${mockDir}`)
          return []
        }

        const files = fs.readdirSync(mockDir).filter(
          f => (f.endsWith('.ts') || f.endsWith('.js')) && !f.startsWith('_')
        )

        const routes: MockMethod[] = []
        for (const file of files) {
          try {
            const filePath = path.join(mockDir, file)
            // 使用 Vite 的 ssrLoadModule 支持 TypeScript 热加载
            const mod = await server.ssrLoadModule(filePath)
            if (mod.default && Array.isArray(mod.default)) {
              routes.push(...mod.default)
            }
          } catch (e) {
            console.error(`[mock] 加载失败: ${file}`, e)
          }
        }

        console.log(`[mock] 已加载 ${routes.length} 条 mock 路由`)
        mockRoutes = routes
        loaded = true
        return routes
      }

      // 监听 mock 文件变更，清除缓存
      server.watcher.add(path.resolve(process.cwd(), mockPath))
      server.watcher.on('change', (file) => {
        if (file.startsWith(path.resolve(process.cwd(), mockPath))) {
          loaded = false
          console.log(`[mock] 文件变更，重新加载: ${path.basename(file)}`)
        }
      })
      server.watcher.on('add', (file) => {
        if (file.startsWith(path.resolve(process.cwd(), mockPath))) {
          loaded = false
        }
      })

      // 参数化路由匹配：支持 /mock-api/admin/brand/:id 格式
      function matchRoute(pattern: string, path: string): { matched: boolean; params: Record<string, string> } {
        // 无参数占位符时精确匹配
        if (!pattern.includes(':')) {
          return { matched: pattern === path, params: {} }
        }
        // 将 :param 转为正则捕获组
        const paramNames: string[] = []
        const regexStr = pattern.replace(/:([^/]+)/g, (_, name) => {
          paramNames.push(name)
          return '([^/]+)'
        })
        const regex = new RegExp('^' + regexStr + '$')
        const match = path.match(regex)
        if (!match) return { matched: false, params: {} }
        const params: Record<string, string> = {}
        paramNames.forEach((name, i) => { params[name] = match[i + 1] })
        return { matched: true, params }
      }

      server.middlewares.use(async (req, res, next) => {
        const routes = await loadMockRoutes()

        // 解析请求信息
        const reqMethod = req.method?.toLowerCase() || 'get'
        const parsedUrl = new URL(req.url || '/', 'http://localhost')
        const reqPath = parsedUrl.pathname

        // 匹配路由：支持精确匹配 + 参数化路由 + method 匹配
        // 参数化路由优先级低于精确匹配，遍历时先匹配到的优先
        let matched: MockMethod | null = null
        let matchedParams: Record<string, string> = {}
        for (const r of routes) {
          const { matched: isMatched, params } = matchRoute(r.url, reqPath)
          if (!isMatched) continue
          if (r.method && r.method.toLowerCase() !== reqMethod) continue
          matched = r
          matchedParams = params
          break
        }

        if (!matched) return next()

        // 构建 MockRequest
        const query: Record<string, string> = {}
        parsedUrl.searchParams.forEach((v, k) => { query[k] = v })

        // 读取请求体（POST/PUT/PATCH）
        const body = await new Promise<any>((resolve) => {
          if (['post', 'put', 'patch'].includes(reqMethod)) {
            const chunks: Buffer[] = []
            req.on('data', (chunk: Buffer) => chunks.push(chunk))
            req.on('end', () => {
              const raw = Buffer.concat(chunks).toString('utf-8')
              try {
                resolve(JSON.parse(raw))
              } catch {
                resolve(raw)
              }
            })
          } else {
            resolve(undefined)
          }
        })

        // 执行 response
        let responseBody: any
        if (typeof matched.response === 'function') {
          const mockReq: MockRequest = { query, body, url: req.url || '', method: reqMethod, params: matchedParams }
          responseBody = matched.response(mockReq)
        } else {
          responseBody = matched.response
        }

        res.setHeader('Content-Type', 'application/json')
        res.statusCode = 200
        res.end(JSON.stringify(responseBody))
      })
    },
  }
}
