import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import axios from 'axios';
import { randomBytes } from 'crypto';

const ILINK_BASE_URL = 'https://ilinkai.weixin.qq.com';
const ILINK_APP_ID = 'bot';
const ILINK_APP_CLIENT_VERSION = (2 << 16) | (2 << 8) | 0;
const CHANNEL_VERSION = '2.2.0';

const EP_GET_UPDATES = 'ilink/bot/getupdates';
const EP_SEND_MESSAGE = 'ilink/bot/sendmessage';
const EP_GET_BOT_QR = 'ilink/bot/get_bot_qrcode';
const EP_GET_QR_STATUS = 'ilink/bot/get_qrcode_status';

const ITEM_TEXT = 1;
const MSG_TYPE_BOT = 2;
const MSG_STATE_FINISH = 2;

export interface WeixinCredential extends Record<string, any> {
  accountId: string;
  token: string;
  baseUrl: string;
  userId?: string;
}

export interface WeixinPendingLogin {
  qrcode: string;
  qrcodeUrl: string;
  currentBaseUrl: string;
  refreshCount: number;
}

@Provide()
@Scope(ScopeEnum.Singleton)
export class NetaClawWeixinService {
  private readonly pendingLogins = new Map<number, WeixinPendingLogin>();

  private json(payload: Record<string, any>) {
    return JSON.stringify(
      {
        ...payload,
        base_info: {
          channel_version: CHANNEL_VERSION,
        },
      },
      null,
      0
    );
  }

  private headers(body: string, token?: string) {
    const raw = randomBytes(4).readUInt32BE(0).toString();
    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
      AuthorizationType: 'ilink_bot_token',
      'Content-Length': String(Buffer.byteLength(body, 'utf-8')),
      'X-WECHAT-UIN': Buffer.from(raw).toString('base64'),
      'iLink-App-Id': ILINK_APP_ID,
      'iLink-App-ClientVersion': String(ILINK_APP_CLIENT_VERSION),
    };
    if (token) {
      headers.Authorization = `Bearer ${token}`;
    }
    return headers;
  }

  private async apiGet(baseUrl: string, endpoint: string) {
    const url = `${baseUrl.replace(/\/$/, '')}/${endpoint}`;
    const { data } = await axios.get(url, {
      timeout: 35000,
      headers: {
        'iLink-App-Id': ILINK_APP_ID,
        'iLink-App-ClientVersion': String(ILINK_APP_CLIENT_VERSION),
      },
    });
    return data || {};
  }

  private async apiPost(baseUrl: string, endpoint: string, payload: Record<string, any>, token?: string) {
    const url = `${baseUrl.replace(/\/$/, '')}/${endpoint}`;
    const body = this.json(payload);
    const { data } = await axios.post(url, body, {
      timeout: 35000,
      headers: this.headers(body, token),
    });
    return data || {};
  }

  async createQrLogin(channelId: number, botType = '3') {
    const data = await this.apiGet(ILINK_BASE_URL, `${EP_GET_BOT_QR}?bot_type=${botType}`);
    const qrcode = String(data.qrcode || '');
    const qrcodeUrl = String(data.qrcode_img_content || '');
    if (!qrcode) {
      throw new Error('微信二维码获取失败');
    }
    this.pendingLogins.set(channelId, {
      qrcode,
      qrcodeUrl,
      currentBaseUrl: ILINK_BASE_URL,
      refreshCount: 0,
    });
    return {
      qrcode,
      qrcodeUrl,
      expiresIn: 480,
    };
  }

  async pollQrLogin(channelId: number) {
    let pending = this.pendingLogins.get(channelId);
    if (!pending) {
      throw new Error('二维码登录会话不存在，请重新获取二维码');
    }

    const readStatus = async () =>
      this.apiGet(
        pending!.currentBaseUrl,
        `${EP_GET_QR_STATUS}?qrcode=${encodeURIComponent(pending!.qrcode)}`
      );

    let data = await readStatus();
    const status = String(data.status || 'wait');

    if (status === 'scaned_but_redirect') {
      const redirectHost = String(data.redirect_host || '');
      if (redirectHost) {
        pending.currentBaseUrl = `https://${redirectHost}`;
        this.pendingLogins.set(channelId, pending);
        data = await readStatus();
      }
    }

    if (String(data.status || '') === 'expired') {
      pending.refreshCount += 1;
      if (pending.refreshCount > 3) {
        this.pendingLogins.delete(channelId);
        return { status: 'expired' };
      }
      const refreshed = await this.apiGet(ILINK_BASE_URL, `${EP_GET_BOT_QR}?bot_type=3`);
      pending = {
        qrcode: String(refreshed.qrcode || ''),
        qrcodeUrl: String(refreshed.qrcode_img_content || ''),
        currentBaseUrl: ILINK_BASE_URL,
        refreshCount: pending.refreshCount,
      };
      this.pendingLogins.set(channelId, pending);
      return {
        status: 'refresh',
        qrcode: pending.qrcode,
        qrcodeUrl: pending.qrcodeUrl,
      };
    }

    if (String(data.status || '') === 'confirmed') {
      const credential: WeixinCredential = {
        accountId: String(data.ilink_bot_id || ''),
        token: String(data.bot_token || ''),
        baseUrl: String(data.baseurl || pending.currentBaseUrl || ILINK_BASE_URL),
        userId: String(data.ilink_user_id || ''),
      };
      if (!credential.accountId || !credential.token) {
        throw new Error('微信登录成功，但凭证不完整');
      }
      this.pendingLogins.delete(channelId);
      return {
        status: 'confirmed',
        credential,
      };
    }

    this.pendingLogins.set(channelId, pending);
    return {
      status: String(data.status || 'wait'),
      qrcode: pending.qrcode,
      qrcodeUrl: pending.qrcodeUrl,
    };
  }

  async getUpdates(credential: WeixinCredential, syncBuf = '') {
    const data = await this.apiPost(
      credential.baseUrl,
      EP_GET_UPDATES,
      { get_updates_buf: syncBuf },
      credential.token
    );
    return {
      syncBuf: String(data.get_updates_buf || syncBuf || ''),
      messages: Array.isArray(data.msgs) ? data.msgs : [],
    };
  }

  async sendText(
    credential: WeixinCredential,
    chatId: string,
    message: string,
    contextToken?: string
  ) {
    const chunks = this.splitMessage(message);
    let lastMessageId = '';

    for (const chunk of chunks) {
      const clientId = `neta-weixin-${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
      const msg: Record<string, any> = {
        from_user_id: '',
        to_user_id: chatId,
        client_id: clientId,
        message_type: MSG_TYPE_BOT,
        message_state: MSG_STATE_FINISH,
        item_list: [{ type: ITEM_TEXT, text_item: { text: chunk } }],
      };
      if (contextToken) {
        msg.context_token = contextToken;
      }
      await this.apiPost(credential.baseUrl, EP_SEND_MESSAGE, { msg }, credential.token);
      lastMessageId = clientId;
    }

    return { success: true, messageId: lastMessageId };
  }

  extractText(itemList: any[]): string {
    if (!Array.isArray(itemList)) return '';
    for (const item of itemList) {
      if (item?.type === ITEM_TEXT) {
        return String(item?.text_item?.text || '').trim();
      }
    }
    return '';
  }

  splitMessage(content: string, maxLength = 3500) {
    const text = String(content || '').trim();
    if (!text) return [''];
    if (text.length <= maxLength) return [text];

    const lines = text.split('\n');
    const chunks: string[] = [];
    let current = '';

    for (const line of lines) {
      const next = current ? `${current}\n${line}` : line;
      if (next.length <= maxLength) {
        current = next;
        continue;
      }
      if (current) {
        chunks.push(current);
        current = '';
      }
      if (line.length <= maxLength) {
        current = line;
        continue;
      }
      for (let i = 0; i < line.length; i += maxLength) {
        chunks.push(line.slice(i, i + maxLength));
      }
    }

    if (current) {
      chunks.push(current);
    }
    return chunks.length > 0 ? chunks : [text];
  }
}
