import http from './http'

/**
 * 与 SimpleLite `MapEditApiController` 对应的前端胶水。
 * 路径前缀：`/sl/projection/map-edit`（YARP → SimpleLite `/projection/map-edit`）。
 *
 * 也覆盖 AI 服务配置 `/sl/projection/ai-config`（GET / POST），由 AiConfigController 提供。
 *
 * 所有方法都通过统一信封 `{ success, code, data, message }` 返回；调用方拿到的是 `data` 部分，
 * 失败时直接抛 Error，让上层 try/catch 或 ElMessage.error 统一处理。
 */

const BASE = '/sl/projection/map-edit'
const AI_BASE = '/sl/projection/ai-config'

export interface MapEditEnvelope<T> {
  success: boolean
  code: number
  data: T | null
  message: string
}

export type MapEditKind =
  | 'site'
  | 'track'
  | 'bezier'
  | 'arc'
  | 'nurbs'
  | 'image'
  | 'text'
  | 'model'
  | 'special'

export interface CreateSitePayload {
  x: number
  y: number
  z?: number
  name?: string
  layer?: string
  [k: string]: unknown
}

export interface CreateTrackPayload {
  siteA: number
  siteB: number
  name?: string
  layer?: string
  [k: string]: unknown
}

export interface CreateImagePayload {
  x: number
  y: number
  imagePath: string
  size?: number
  angle?: number
  opacity?: number
  name?: string
  layer?: string
}

export interface CreateTextPayload {
  x: number
  y: number
  content: string
  color?: string
  size?: number
  name?: string
  layer?: string
}

export interface CreateModelPayload {
  x: number
  y: number
  modelPath: string
  scale?: number
  rotation?: number
  zOffset?: number
  name?: string
  layer?: string
}

export interface CreateCarPayload {
  x: number
  y: number
  /** 朝向，弧度。可省略，默认 0。 */
  theta?: number
  name?: string
  layer?: string
}

export interface BatchOp {
  action: 'create' | 'delete' | 'patch'
  kind?: MapEditKind | string
  id?: number
  data?: Record<string, unknown>
}

export interface PickResult {
  x: number
  y: number
  siteId: number
}

export interface DashboardSummary {
  cars: number
  onlineCars: number
  alarmingCars: number
  sites: number
  tracks: number
  specials: number
  maps: number
  missions: number
  timestamp: string
}

export interface AssetUploadResult {
  fileName: string
  savedPath: string
  /** 相对 url，前端拼上 webVRender host 即可显示。 */
  url: string
}

export interface ViewFilterSnapshot {
  selectFilter: { sites: boolean; tracks: boolean; cars: boolean; decor: boolean }
  alignSnap: { sites: boolean; cars: boolean; tracks: boolean }
  showViewport: { sceneLabels: boolean; scenePrimitives: boolean; cars: boolean }
}

/** 部分更新；只传你要改的字段即可。后端不会覆盖未传字段。 */
export interface ViewFilterPatch {
  selectFilter?: Partial<ViewFilterSnapshot['selectFilter']>
  alignSnap?: Partial<ViewFilterSnapshot['alignSnap']>
  showViewport?: Partial<ViewFilterSnapshot['showViewport']>
}

export interface AiMapGenerateRequest {
  prompt: string
  mode?: 'sites' | 'tracks' | 'sites+tracks' | 'full'
  bounds?: [number, number, number, number]
  referenceImageBase64?: string
  layer?: string
  constraints?: string
}

export interface AiMapGenerateResult {
  created: Array<{ tool: string; kind: string; id: number; typeName?: string; error?: string }>
  assistantText?: string
  usedTools: number
}

export interface AiConfig {
  endpoint: string
  apiKey: string
  model: string
  systemPrompt: string
  temperature: number
  maxTokens: number
  timeoutSec: number
  /** 仅 GET 返回时携带：apiKey 非空 → true。 */
  configured?: boolean
}

async function unwrap<T>(promise: Promise<{ data: MapEditEnvelope<T> }>): Promise<T> {
  const { data } = await promise
  if (!data?.success) throw new Error(data?.message ?? 'request failed')
  return data.data as T
}

export const mapEditApi = {
  // 对象创建/删除
  createSite: (p: CreateSitePayload) =>
    unwrap<{ kind: 'site'; id: number; name?: string }>(http.post(`${BASE}/objects/site`, p)),

  createTrack: (kind: 'track' | 'bezier' | 'arc' | 'nurbs', p: CreateTrackPayload) =>
    unwrap<{ kind: 'track'; id: number; typeName: string }>(http.post(`${BASE}/objects/${kind}`, p)),

  createImage: (p: CreateImagePayload) =>
    unwrap<{ kind: 'image'; id: number }>(http.post(`${BASE}/objects/image`, p)),

  createText: (p: CreateTextPayload) =>
    unwrap<{ kind: 'text'; id: number }>(http.post(`${BASE}/objects/text`, p)),

  createModel: (p: CreateModelPayload) =>
    unwrap<{ kind: 'model'; id: number }>(http.post(`${BASE}/objects/model`, p)),

  /** 直接创建一辆模拟车 (DummyCar)，不经过画布 pick。 */
  createCar: (p: CreateCarPayload) =>
    unwrap<{ kind: 'car'; id: number; typeName: string }>(http.post(`${BASE}/objects/car`, p)),

  deleteObject: (kind: MapEditKind | string, id: number) =>
    unwrap<{ kind: string; id: number; deleted: boolean }>(http.delete(`${BASE}/objects/${kind}/${id}`)),

  batch: (ops: BatchOp[]) =>
    unwrap<{ count: number; results: unknown[] }>(http.post(`${BASE}/objects/batch`, { ops })),

  copyFieldsTo: (kind: MapEditKind | string, id: number, fieldNames: string[], targets: Array<{ kind: string; id: number }>) =>
    unwrap<{ copied: number }>(http.post(`${BASE}/objects/${kind}/${id}/fields/copy-to`, { fieldNames, targets })),

  // 拾取 / 仪表盘
  pick: () => unwrap<PickResult>(http.post(`${BASE}/pick`)),

  dashboardSummary: () =>
    unwrap<DashboardSummary>(http.get(`${BASE}/dashboard/summary`)),

  // 资产上传：传 base64
  uploadAsset: (filename: string, dataBase64: string) =>
    unwrap<AssetUploadResult>(http.post(`${BASE}/assets/upload`, { filename, data: dataBase64 }, {
      headers: { 'Content-Type': 'multipart/form-data' }
    })),

  // AI 生图
  aiMapGenerate: (req: AiMapGenerateRequest) =>
    unwrap<AiMapGenerateResult>(http.post(`${BASE}/ai/map-generate`, req)),

  projectSave: (path?: string) =>
    unwrap<{ path: string; savedAt: string }>(http.post(`${BASE}/project/save`, { path: path ?? '' })),

  projectLoad: (path: string) =>
    unwrap<{ path: string; sites: number; tracks: number; specials: number; missions: number }>(
      http.post(`${BASE}/project/load`, { path })
    ),

  projectCurrent: () =>
    unwrap<{ sites: number; tracks: number; specials: number; maps: number; missions: number; timestamp: string; cwd: string }>(
      http.get(`${BASE}/project/current`)
    ),

  projectBrowse: (dir?: string) =>
    unwrap<{
      dir: string
      parent: string | null
      subdirs: Array<{ name: string; fullPath: string }>
      jsons: Array<{ name: string; fullPath: string; sizeBytes: number; modified: string }>
    }>(http.get(`${BASE}/project/browse`, { params: dir ? { dir } : {} })),

  /**
   * 在 SimpleLite 桌面进程上弹 Windows 原生「打开文件」对话框，
   * 让用户挑一个项目 JSON。返回 { path, cancelled }；cancelled=true 时 path 为 null。
   * initialDir 默认 SimpleLite 程序工作目录。
   */
  projectNativePickOpen: (initialDir?: string) =>
    unwrap<{ path: string | null; cancelled: boolean }>(
      http.post(`${BASE}/project/native-pick-open`, { initialDir: initialDir ?? '' })
    ),

  /**
   * 弹 Windows 原生「另存为」对话框。返回 { path, cancelled }。
   * suggestedFileName 默认 project.current.json。
   */
  projectNativePickSave: (initialDir?: string, suggestedFileName?: string) =>
    unwrap<{ path: string | null; cancelled: boolean }>(
      http.post(`${BASE}/project/native-pick-save`, {
        initialDir: initialDir ?? '',
        suggestedFileName: suggestedFileName ?? 'project.current.json'
      })
    ),

  sampleSitesAlongTrack: (trackId: number, opts: { spacingMm: number; includeEndpoints?: boolean; layer?: string }) =>
    unwrap<{ created: number; ids: number[] }>(
      http.post(`${BASE}/objects/track/${trackId}/sample-sites`, opts)
    ),

  /**
   * 读取后端「过滤」菜单当前的三组开关。编辑器载入时拉一次，让前端 reactive 状态
   * 与 SimpleLite 真实开关一致（避免「前端勾着、后端没改」的偏差）。
   */
  getViewFilter: () => unwrap<ViewFilterSnapshot>(http.get(`${BASE}/view-filter`)),

  /**
   * 部分更新「过滤」菜单。只传要改的字段即可，未传字段保持原状。
   * 后端写入对应 SimpleUI 静态字段 + 触发 SimpleSceneRenderer.NotifyStateChanged()
   * 立即重画工作区，对 SelectFilter 变化还会同步 CycleGUI 引擎层 SelectObject 模式。
   */
  setViewFilter: (patch: ViewFilterPatch) =>
    unwrap<ViewFilterSnapshot>(http.post(`${BASE}/view-filter`, patch))
}

export const aiConfigApi = {
  get: () => unwrap<AiConfig>(http.get(`${AI_BASE}/`)),
  save: (cfg: AiConfig) => unwrap<{ saved: boolean }>(http.post(`${AI_BASE}/`, cfg))
}
