import { Injectable, Logger } from '@nestjs/common'
import { Redis } from 'ioredis'
import { RedisService } from 'nestjs-redis'
import {
  AirDailyForecastItem,
  AirNow,
  CityInfo,
  DailyForecastItem,
  HourlyForecastItem,
  LivingIndexItem,
  RainSurvey,
  WarningCity,
  WarningNowItem,
  WeatherNow,
} from './hefeng-http.model'
import { HefengHttpService } from './hefeng-http.service'

/**
 * ### 模块说明
 *
 * ```markdown
 * 1. 与 `HefengHttpService` 中的方法一一对应,逐个增加 `缓存处理`。
 * 2. 缓存存入 `Redis` 中。
 * 3. 每个 API 的缓存时效均不同。
 * ```
 *
 *
 * ### 注意事项
 *
 * ```markdown
 * 1. 各个请求的 `请求参数` 以及函数的格式几乎完全一致,可以封装成 `type -> apiInfo` 的形式去请求接口,只需要一个函数就够了,
 * 而不是每一个对应的 API 都使用了一个函数(当前文件的方案),但千万不要这么做。
 * 2. 上述做法的缺点是都写在一个函数内,里面需要有各种冗余的判断,会被限制住,处理差异性参数非常麻烦。
 * 3. 当前版本就是从上述的版本改过来的,不要再重蹈覆辙了(宁可每个方法都有很多重复的代码),这样一劳永逸。
 * ```
 */
@Injectable()
export class HefengCachedService {
  /** 日志工具 */
  private readonly logger = new Logger(HefengCachedService.name)

  private readonly redis: Redis

  constructor(private redisService: RedisService, private readonly hefengHttpService: HefengHttpService) {
    this.redis = this.redisService.getClient()
  }

  /**
   * 查询和风天气中的城市信息
   *
   * @param location 位置
   */
  async searchCity(location: string): Promise<CityInfo[]> {
    /** Redis 键名 */
    const rKey = `hefeng:city:location:${location}`

    /** 缓存时长:10天 */
    const expiration = 60 * 60 * 24 * 10

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.searchCity(location)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 热门城市查询
   */
  async getTopCity(): Promise<CityInfo[]> {
    /** Redis 键名 */
    const rKey = `hefeng:city:top_city`

    /** 缓存时长:10分钟 */
    const expiration = 60 * 10

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getTopCity()
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取实时天气数据
   *
   * @param location 需要查询地区的 `LocationID` 或以英文逗号分隔的 `经度,纬度` 坐标(十进制)
   */
  async getWeatherNow(location: string): Promise<WeatherNow> {
    /** Redis 键名 */
    const rKey = `hefeng:now:location:${location}`

    /** 缓存时长:10分钟 */
    const expiration = 60 * 10

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getWeatherNow(location)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取逐天天气预报
   *
   * @param location 需要查询地区的 `LocationID` 或以英文逗号分隔的 `经度,纬度` 坐标(十进制)
   * @param days 天数
   */
  async getDailyForecast(location: string, days: 3 | 7 | 10 | 15): Promise<DailyForecastItem[]> {
    /** Redis 键名 */
    const rKey = `hefeng:daily_${days}d:location:${location}`

    /** 缓存时长:2小时 */
    const expiration = 60 * 60 * 2

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getDailyForecast(location, days)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取逐小时天气预报
   *
   * @param location 需要查询地区的 `LocationID` 或以英文逗号分隔的 `经度,纬度` 坐标(十进制)
   * @param hours 小时数
   */
  async getHourlyForecast(location: string, hours: 24 | 72 | 168): Promise<HourlyForecastItem[]> {
    /** Redis 键名 */
    const rKey = `hefeng:hourly_${hours}h:location:${location}`

    /** 缓存时长:10分钟 */
    const expiration = 60 * 10

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getHourlyForecast(location, hours)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取天气生活指数
   *
   * @param location 需要查询地区的 `LocationID` 或以英文逗号分隔的 `经度,纬度` 坐标(十进制)
   */
  async getLivingIndex(location: string): Promise<LivingIndexItem[]> {
    /** Redis 键名 */
    const rKey = `hefeng:living_index:location:${location}`

    /** 缓存时长:2 小时 */
    const expiration = 60 * 60 * 2

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getLivingIndex(location)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取实时空气质量
   *
   * @param location 需要查询地区的 `LocationID` 或以英文逗号分隔的 `经度,纬度` 坐标(十进制)
   */
  async getAirNow(location: string): Promise<AirNow> {
    /** Redis 键名 */
    const rKey = `hefeng:air_now:location:${location}`

    /** 缓存时长:10分钟 */
    const expiration = 60 * 10

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getAirNow(location)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取空气质量预报
   *
   * @param location 需要查询地区的 `LocationID` 或以英文逗号分隔的 `经度,纬度` 坐标(十进制)
   */
  async getAirDailyForecast(location: string): Promise<AirDailyForecastItem[]> {
    /** Redis 键名 */
    const rKey = `hefeng:air_daily:location:${location}`

    /** 缓存时长:4小时 */
    const expiration = 60 * 60 * 4

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getAirDailyForecast(location)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取分钟级降水
   *
   * @param location 需要查询地区的以英文逗号分隔的 `经度,纬度` 坐标(十进制)
   */
  async getMinutelyRain(location: string): Promise<RainSurvey> {
    /** Redis 键名 */
    const rKey = `hefeng:rain:location:${location}`

    /** 缓存时长:10分钟 */
    const expiration = 60 * 10

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getMinutelyRain(location)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取天气预警城市列表
   */
  async getWarningCityList(): Promise<WarningCity[]> {
    /** Redis 键名 */
    const rKey = `hefeng:city:warning_city`

    /** 缓存时长:10分钟 */
    const expiration = 60 * 10

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getWarningCityList()
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }

  /**
   * 获取天气灾害预警
   *
   * @param location 需要查询地区的 `LocationID` 或以英文逗号分隔的 `经度,纬度` 坐标(十进制)
   */
  async getWarningNow(location: string): Promise<WarningNowItem[]> {
    /** Redis 键名 */
    const rKey = `hefeng:warning_now:location:${location}`

    /** 缓存时长:10分钟 */
    const expiration = 60 * 10

    const result = await this.redis.get(rKey)
    if (result) {
      return JSON.parse(result)
    } else {
      const res = await this.hefengHttpService.getWarningNow(location)
      await this.redis.set(rKey, JSON.stringify(res), 'EX', expiration)
      return res
    }
  }
}