/**
 * VLM 客户端 — 走 model_channel 拿凭据，OpenAI 兼容多模态调用。
 *
 * 公开 API（被 adapter / runtime 调用）：
 *  - verifyState(ctx, question, screenshot)：简单是非验证，内部用 parser.parseVerify
 *  - nextAction(ctx, parser, screenshot, history)：让模型决定下一步（MVP 暂不用 — adapter 硬编码步骤）
 *
 * 资费/性能：history 只传"上一张 + 最新一张"截图（plan v4 §10 截断要求）
 */

import { Provide, Scope, ScopeEnum, Inject } from '@midwayjs/core';
import OpenAI from 'openai';
import type { AdapterContext, ActionStep, DesktopTask } from './types.js';
import type { Parser } from './parser/parser.js';
import { NetaClawModelChannelService } from '../../netaclaw/service/model_channel.js';

/** VLM 调用历史条目，含截图与可选思考文本 */
export interface VlmHistoryEntry {
  screenshot: Buffer;
  thought?: string;
}

@Provide()
@Scope(ScopeEnum.Singleton)
export class VlmClient {
  @Inject()
  modelChannelService!: NetaClawModelChannelService;

  /**
   * 状态验证 — 给模型一张截图 + 问题，期望返回布尔
   * 1 次模型调用，ctx.modelCalls++（若 ctx 存在）
   */
  async verifyState(ctx: AdapterContext | null, question: string, screenshot: Buffer): Promise<boolean> {
    const modelChannelId = this.resolveModelChannelId(ctx);
    const channel = await this.fetchChannel(modelChannelId);
    const client = this.makeClient(channel);
    const modelName = this.resolveModelName(channel);

    const systemPrompt =
      '你是 GUI 状态验证助手。根据用户提供的截图回答 yes / no，如果不确定请回答 no。可附 reason。优先输出 JSON: {"verified": true|false, "reason": "..."}.';

    const userContent: any[] = [
      { type: 'text', text: question },
      {
        type: 'image_url',
        image_url: { url: 'data:image/png;base64,' + screenshot.toString('base64') },
      },
    ];

    const resp = await client.chat.completions.create({
      model: modelName,
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: userContent },
      ],
      max_tokens: 200,
      temperature: 0,
    });

    // 累计模型调用次数
    if (ctx) ctx.modelCalls = (ctx.modelCalls ?? 0) + 1;

    const raw = resp.choices?.[0]?.message?.content ?? '';
    const text = typeof raw === 'string' ? raw : JSON.stringify(raw);

    // 优先使用 ctx.parser.parseVerify，否则用本地兜底
    if (ctx?.parser && typeof (ctx.parser as any).parseVerify === 'function') {
      return (ctx.parser as Parser).parseVerify(text);
    }
    return this.defaultParseVerify(text);
  }

  /**
   * 通用下一步动作选择（MVP 暂未使用，留口）
   * history 只取最后一条（plan v4 §10：上一张 + 最新一张）
   */
  async nextAction(
    ctx: AdapterContext,
    parser: Parser,
    screenshot: Buffer,
    history: VlmHistoryEntry[],
  ): Promise<{ action: ActionStep; tokensUsed: number }> {
    const modelChannelId = this.resolveModelChannelId(ctx);
    const channel = await this.fetchChannel(modelChannelId);
    const client = this.makeClient(channel);
    const modelName = this.resolveModelName(channel);

    const systemPrompt = parser.buildSystemPrompt(ctx.task as DesktopTask, ctx);

    // 截断：只保留最近一条历史帧
    const truncatedHistory = history.slice(-1);

    const messages: any[] = [{ role: 'system', content: systemPrompt }];

    // 附加历史帧（最多 1 条）
    for (const h of truncatedHistory) {
      messages.push({
        role: 'user',
        content: [
          {
            type: 'text',
            text: h.thought ? '上一步思考: ' + h.thought : '上一帧截图',
          },
          {
            type: 'image_url',
            image_url: { url: 'data:image/png;base64,' + h.screenshot.toString('base64') },
          },
        ],
      });
    }

    // 附加当前帧
    messages.push({
      role: 'user',
      content: [
        { type: 'text', text: '请根据当前截图给出下一步 JSON action。' },
        {
          type: 'image_url',
          image_url: { url: 'data:image/png;base64,' + screenshot.toString('base64') },
        },
      ],
    });

    const resp = await client.chat.completions.create({
      model: modelName,
      messages,
      max_tokens: 400,
      temperature: 0,
    });

    // 累计模型调用次数
    ctx.modelCalls = (ctx.modelCalls ?? 0) + 1;

    const raw = resp.choices?.[0]?.message?.content ?? '';
    const text = typeof raw === 'string' ? raw : JSON.stringify(raw);

    const action = parser.parseAction(text);
    const tokensUsed = resp.usage?.total_tokens ?? 0;

    return { action, tokensUsed };
  }

  /** 解析当前任务对应的 modelChannelId（从 ctx.task.modelChannelId） */
  private resolveModelChannelId(ctx: AdapterContext | null): number {
    const id = ctx?.task?.modelChannelId;
    if (!id || typeof id !== 'number') {
      throw new Error('model-channel-not-configured');
    }
    return id;
  }

  /** 通过 modelChannelService 获取渠道配置 */
  private async fetchChannel(id: number): Promise<{ baseUrl: string; apiKey: string; models: any[] }> {
    // 优先调 .info(id)，fallback 到 channelRepo.findOneBy
    const svc: any = this.modelChannelService;
    let row: any = null;
    if (typeof svc.info === 'function') {
      row = await svc.info(id);
    } else if (svc.channelRepo) {
      row = await svc.channelRepo.findOneBy({ id });
    }
    if (!row) throw new Error('model-channel-not-found: ' + id);
    return { baseUrl: row.baseUrl, apiKey: row.apiKey, models: row.models ?? [] };
  }

  /** 选模型名：取 channel.models 第一个有 vision/multimodal capability，否则取第一个，否则报错 */
  private resolveModelName(channel: { models: any[] }): string {
    if (!channel.models || channel.models.length === 0) {
      throw new Error('model-channel-no-models');
    }
    const visionFirst = channel.models.find(
      m => m?.capability === 'vision' || m?.capability === 'multimodal',
    );
    return (visionFirst ?? channel.models[0]).name;
  }

  /** 构造 OpenAI 兼容 client */
  private makeClient(channel: { baseUrl: string; apiKey: string }) {
    return new OpenAI({ baseURL: channel.baseUrl, apiKey: channel.apiKey });
  }

  /** ctx.parser 不可用时的兜底 verify 解析 */
  private defaultParseVerify(text: string): boolean {
    const t = text.trim().toLowerCase();
    try {
      // 尝试解析 JSON
      const m = text.match(/\{[\s\S]*\}/);
      if (m) {
        const obj = JSON.parse(m[0]);
        if (typeof obj.verified === 'boolean') return obj.verified;
      }
    } catch {}
    if (t.includes('"verified": true') || t.includes('"verified":true')) return true;
    if (t.includes('"verified": false') || t.includes('"verified":false')) return false;
    if (t.startsWith('yes') || t.includes('是的') || t === '是') return true;
    if (t.startsWith('no') || t.includes('不是') || t === '否') return false;
    return false;
  }
}
