import { Provide, Inject } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import axios from 'axios';
import { BaseSysParamEntity } from '../../base/entity/sys/param';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';


interface Ys7TokenData {
  accessToken: string;
  /** 萤石返回的毫秒级过期时间戳 */
  expireTime: number;
}

@Provide()
export class Ys7Service extends BaseService {
  @InjectEntityModel(BaseSysParamEntity)
  baseSysParamEntity: Repository<BaseSysParamEntity>;

  /** 内存并发锁 */
  private refreshing: Promise<Ys7TokenData> | null = null;

  /**
   * 对外暴露：拿 token（自动读缓存 / 续签）
   */
  async getAccessToken() {
    const token = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
    if (
      !token ||
      !token.updateTime ||
      Date.now() - new Date(token.updateTime).getTime() > 6 * 24 * 60 * 60 * 1000
    ) {
      await this.fetchAndSaveRemote();
    }
  }

  /** 真正调萤石 */
  private async fetchAndSaveRemote(): Promise<Ys7TokenData> {
    const appKey_result = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.appKey' });
    const appSecret_result = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.appSecret' });
    if (!appKey_result || !appSecret_result) {
      throw new Error(
        '萤石云 appKey/appSecret 未配置，请在【系统参数】中维护 ys7.appKey / ys7.appSecret'
      );
    }
    const { data: res } = await axios.post(
      'https://open.ys7.com/api/lapp/token/get',
      new URLSearchParams({ appKey: appKey_result.data, appSecret: appSecret_result.data }),
      {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        timeout: 8000,
      }
    );
    if (String(res.code) !== '200') {
      throw new Error(`萤石云获取 Token 失败：${res.msg}（code: ${res.code}）`);
    }

    const tokenData: Ys7TokenData = {
      accessToken: res.data.accessToken,
      expireTime: res.data.expireTime,
    };
    const token = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
    this.baseSysParamEntity.save({
      ...token,
      data: tokenData.accessToken,
      updateTime: new Date(),
    }).catch(() => {
      // 忽略写回失败
    });
    return tokenData;
  }

  async getSpaceID(): Promise<string> {
    const entity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.storageSpaceID' });
    if (!entity ||
      !entity.updateTime ||
      Date.now() - new Date(entity.updateTime).getTime() >
      6 * 24 * 60 * 60 * 1000) {
      const spaceId = await this.createCaptureSpace();
      await this.baseSysParamEntity.save({
        id: entity?.id,
        keyName: 'ys7.storageSpaceID',
        data: spaceId,
        updateTime: new Date(),
      });
      return spaceId;
    }
    return entity!.data as string;
  }

  /**
   * 创建萤石云【设备抓图】存储空间
   * - spaceName 取自系统参数 ys7.spaceName
   * - 过期天数默认 7 天
   * - 创建成功后 storageSpaceID 写入 ys7.storageSpaceID
   * - 调用萤石云空间创建接口：https://open.ys7.com/api/service/open/storage/engine/space
   * - 接口说明地址：https://open.ys7.com/help/5236
   * - 将创建好的存储空间ID存入参数：ys7.storageSpaceID
   */
  async createCaptureSpace(expireDays = 7): Promise<string> {
    // 1. accessToken
    const tokenEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
    if (!tokenEntity?.data) {
      console.error('[Ys7Service] ys7.accessToken 为空');
      throw new Error('ys7.accessToken 为空');
    }
    const accessToken = String(tokenEntity.data).trim();

    // 2. spaceName
    const spaceEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.spaceName' });
    if (!spaceEntity?.data) {
      console.error('[Ys7Service] ys7.spaceName 未配置');
      throw new Error('ys7.spaceName 未配置');
    }
    const spaceName = String(spaceEntity.data).trim();
    // 4. 萤石侧是否已存在同名空间
    const existId = await this.getSpaceIdByName(accessToken, spaceName);
    if (existId) {
      console.log('[Ys7Service] 萤石侧已存在同名空间，spaceId=%s', existId);
      await this.saveStorageSpaceID(existId);
      return existId;
    }
    // 5. 真正创建
    const params = new URLSearchParams({
      bizType: 'capture',
      expireDays: String(expireDays),
      spaceName,
      storageType: '1',
    });

    let res: any;
    try {
      const resp = await axios.post(
        'https://open.ys7.com/api/service/open/storage/engine/space',
        params,
        {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            accessToken,
          },
          timeout: 8000,
        }
      );
      res = resp.data;
    } catch (err: any) {
      console.error('[Ys7Service] 萤石接口请求失败');
      if (err.response) {
        console.error('[Ys7Service] RESPONSE BODY=%s', JSON.stringify(err.response.data));
      } else {
        console.error('[Ys7Service] ERROR MSG=%s', err.message);
      }
      throw err;
    }
    // 6. 校验萤石业务返回
    if (!res || res.meta?.code !== 200) {
      console.error('[Ys7Service] 萤石创建空间业务失败: %s', JSON.stringify(res?.meta));
      throw new Error(`萤石创建空间失败: ${JSON.stringify(res?.meta)}`);
    }
    const storageSpaceID = String(res.data); // data 就是 spaceId
    // 7. 保存到系统参数
    await this.saveStorageSpaceID(storageSpaceID);
    return storageSpaceID;
  }

  /**
   * 修改萤石云【设备抓图】存储空间
   * - spaceId 取自 ys7.storageSpaceID
   * - accessToken 取自 ys7.accessToken
   * - 默认过期天数 7 天
   */
  async updateCaptureSpace(expireDays = 7): Promise<boolean> {
    // 1. accessToken
    const tokenEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
    if (!tokenEntity?.data) {
      throw new Error('萤石 accessToken 不存在，请先获取 Token');
    }
    const accessToken = tokenEntity.data as string;

    // 2. spaceId
    const spaceEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.storageSpaceID' });
    if (!spaceEntity?.data) {
      throw new Error('萤石 storageSpaceID 不存在，请先创建抓图空间');
    }
    const spaceId = Number(spaceEntity.data);

    // 3. 组装参数（PUT 使用 URLSearchParams）
    const params = new URLSearchParams();
    params.append('spaceId', String(spaceId));
    params.append('expireDays', String(expireDays));

    const url = 'https://open.ys7.com/api/service/open/storage/engine/space';
    const { data: res } = await axios.put(url, params, {
      headers: {
        accessToken,
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      timeout: 8000,
    });
    if (res.meta?.code !== 200) {
      throw new Error(
        `萤石云修改存储空间失败：${res.meta?.message}（code: ${res.meta?.code}）`
      );
    }
    if (res.data) {
      this.baseSysParamEntity.save({
        ...spaceEntity,
        updateTime: new Date(),
      }).catch(() => {
        // 忽略写回失败
      });
    }
    return !!res.data;
  }

  /**
   * 删除萤石云【设备抓图】存储空间
   * - spaceId 取自 ys7.storageSpaceID
   * - accessToken 取自 ys7.accessToken
   * - 删除成功后，清空 ys7.storageSpaceID
   */
  async deleteCaptureSpace(): Promise<boolean> {
    // 1. accessToken
    const tokenEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
    if (!tokenEntity?.data) {
      throw new Error('萤石 accessToken 不存在，请先获取 Token');
    }
    const accessToken = tokenEntity.data as string;

    // 2. spaceId
    const spaceEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.storageSpaceID' });
    if (!spaceEntity?.data) {
      throw new Error('萤石 storageSpaceID 不存在，无法删除');
    }
    const spaceId = Number(spaceEntity.data);

    // 3. DELETE 请求（body 使用 URLSearchParams）
    const params = new URLSearchParams();
    params.append('spaceId', String(spaceId));

    const url = 'https://open.ys7.com/api/service/open/storage/engine/space';
    const { data: res } = await axios.delete(url, {
      headers: {
        accessToken,
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      data: params.toString(),
      timeout: 8000,
    });
    if (res.meta?.code !== 200) {
      throw new Error(
        `萤石云删除存储空间失败：${res.meta?.message}（code: ${res.meta?.code}）`
      );
    }
    // 4. 清理本地参数
    spaceEntity.data = '';
    spaceEntity.updateTime = new Date();
    await this.baseSysParamEntity.save(spaceEntity);

    return !!res.data;
  }

  /**
   * 根据 spaceName 查询萤石是否已存在【设备抓图】空间
   * 使用官方 listById 接口，支持翻页
   * 返回 storageSpaceID 或 null
   */
  private async getSpaceIdByName(
    accessToken: string,
    spaceName: string
  ): Promise<string | null> {

    const url = 'https://open.ys7.com/api/service/open/storage/engine/space/listById';

    // 查询时间窗口：最近 30 天（萤石强制要求）
    const endTime = new Date();
    const startTime = new Date(endTime.getTime() - 30 * 24 * 60 * 60 * 1000);

    const format = (d: Date) =>
      d.getFullYear() +
      '-' +
      String(d.getMonth() + 1).padStart(2, '0') +
      '-' +
      String(d.getDate()).padStart(2, '0') +
      ' ' +
      String(d.getHours()).padStart(2, '0') +
      ':' +
      String(d.getMinutes()).padStart(2, '0') +
      ':' +
      String(d.getSeconds()).padStart(2, '0');

    const queryParams = new URLSearchParams({
      startTime: format(startTime),
      endTime: format(endTime),
      pageSize: '50',
      bizTypeList: 'capture', // 只查设备抓图空间
    });

    let lastId: number | null = null;
    let page = 1;

    try {
      while (true) {
        if (lastId !== null) {
          queryParams.set('lastSpaceId', String(lastId));
        }

        const fullUrl = `${url}?${queryParams.toString()}`;

        const resp = await axios.get(fullUrl, {
          headers: {
            accessToken,
          },
          timeout: 8000,
        });
        const res = resp.data;
        if (res?.meta?.code !== 200) {
          console.error('[Ys7Service] 查询空间列表失败: %s', JSON.stringify(res.meta));
          throw new Error(`查询萤石空间失败: ${JSON.stringify(res.meta)}`);
        }

        const data = res.data || {};
        const list = data.result || [];

        // 精确匹配 spaceName
        const match = list.find(
          (item: any) => item.spaceName === spaceName && item.bizType === 'capture'
        );

        if (match) {
          console.log(
            '[Ys7Service] ✅ 找到已存在的空间：spaceId=%s, spaceName=%s, bizType=%s',
            match.spaceId,
            match.spaceName,
            match.bizType
          );
          return String(match.spaceId);
        }

        // 没有下一页，终止
        if (!data.hasNext) {
          break;
        }

        lastId = data.lastId;
        page++;
      }
      return null;
    } catch (err: any) {
      console.error('[Ys7Service] 查询萤石空间异常');
      if (err.response) {
        console.error('[Ys7Service] HTTP STATUS=%d', err.response.status);
        console.error('[Ys7Service] RESPONSE=%s', JSON.stringify(err.response.data));
      } else {
        console.error('[Ys7Service] ERROR=%s', err.message);
      }
      throw err;
    }
  }

  /** 公共保存方法 */
  private async saveStorageSpaceID(spaceId: string) {
    let e = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.storageSpaceID' });
    if (!e) {
      e = this.baseSysParamEntity.create({
        keyName: 'ys7.storageSpaceID',
        name: '萤石云抓图空间ID',
        dataType: 1,
        data: spaceId,
        updateTime: new Date(),
      });
    } else {
      e.data = spaceId;
      e.updateTime = new Date();
    }
    await this.baseSysParamEntity.save(e);
  }

}