/**
 * geo-weather.js
 *
 * 启动时自动采集位置坐标 + 实时天气，落盘缓存，注入 system prompt。
 *
 * 数据分两层：
 *   location  — 坐标 + 地址详情，IP 未变时最长 7 天复用缓存
 *   weather   — 当前天气 + 今明两天，每次启动刷新
 *
 * 数据来源（全部免费，无需 API Key）：
 *   ip-api.com          → 公网 IP 反查坐标 / 城市 / 时区
 *   nominatim (OSM)     → 坐标反解详细地址（区级）
 *   wttr.in             → 天气（当前 + 今明两天）
 *
 * 对外接口：
 *   collectGeoWeather()    → 启动时调用一次，async
 *   getGeoWeatherBlock()   → 返回注入 prompt 的纯文本块，同步
 */

import fs from 'fs'
import path from 'path'
import { paths } from './paths.js'

const GEO_WEATHER_FILE = path.join(paths.dataDir, 'geo-weather.json')
const GEO_WEATHER_VERSION = 1
const LOCATION_REFRESH_MS = 7 * 24 * 60 * 60 * 1000  // 位置信息兜底 7d 刷新一次

let _cached = null

// ─── 工具函数 ──────────────────────────────────────────────────────────────────

function safe(fn, fallback = null) {
  try { return fn() } catch { return fallback }
}

async function fetchJSON(url, options = {}, timeoutMs = 8000) {
  try {
    const ctrl = new AbortController()
    const t = setTimeout(() => ctrl.abort(), timeoutMs)
    const res = await fetch(url, { signal: ctrl.signal, ...options })
    clearTimeout(t)
    if (!res.ok) return null
    return await res.json()
  } catch {
    return null
  }
}

// ─── 位置采集 ─────────────────────────────────────────────────────────────────

async function fetchLocation() {
  // Step 1：IP 反查坐标（同时拿到城市 / 时区 / ISP）
  const ipData = await fetchJSON(
    'http://ip-api.com/json/?fields=status,query,city,regionName,country,countryCode,lat,lon,timezone,isp'
  )
  if (!ipData || ipData.status !== 'success') {
    console.warn('[geo-weather] ip-api.com 定位失败')
    return null
  }

  const { lat, lon, city, regionName, country, countryCode, timezone, isp, query: ip } = ipData

  // Step 2：Nominatim 反解精细地址（区 / 街道级）
  // User-Agent 是 Nominatim 免费使用协议要求
  const nominatim = await fetchJSON(
    'https://nominatim.openstreetmap.org/reverse?lat=' + lat + '&lon=' + lon + '&format=json&accept-language=zh',
    { headers: { 'User-Agent': 'BaiLongma/2.0 (personal-assistant; contact: user@example.com)' } },
    10000
  )

  const addr = nominatim?.address || {}

  return {
    lat: Number(lat),
    lon: Number(lon),
    ip,
    isp: isp || null,
    city:         addr.city || addr.town || addr.village || city,
    district:     addr.suburb || addr.district || addr.county || null,
    region:       addr.state || regionName,
    country:      addr.country || country,
    country_code: (addr.country_code || countryCode || '').toUpperCase(),
    timezone:     timezone || null,
    display_name: nominatim?.display_name || (city + ', ' + regionName + ', ' + country),
    collected_at: new Date().toISOString(),
  }
}

// ─── 天气采集 ─────────────────────────────────────────────────────────────────

function parseWttrDay(day) {
  if (!day) return null
  // hourly 索引 4 ≈ 12:00，作为当天代表天气描述
  const noon = day.hourly?.[4]
  const condition = noon?.lang_zh?.[0]?.value || noon?.weatherDesc?.[0]?.value
    || day.hourly?.[0]?.lang_zh?.[0]?.value || ''
  return {
    date:      day.date || '',
    high:      Number(day.maxtempC),
    low:       Number(day.mintempC),
    condition,
    sunrise:   day.astronomy?.[0]?.sunrise || '',
    sunset:    day.astronomy?.[0]?.sunset  || '',
  }
}

async function fetchWeather(lat, lon, city) {
  // 优先用坐标精确定位，坐标缺失时降级到城市名
  const query = (lat != null && lon != null)
    ? (lat + ',' + lon)
    : encodeURIComponent(city || 'Beijing')

  const data = await fetchJSON(
    'https://wttr.in/' + query + '?format=j1&lang=zh',
    {},
    12000
  )
  if (!data) {
    console.warn('[geo-weather] wttr.in 天气获取失败')
    return null
  }

  const cur = data.current_condition?.[0]
  if (!cur) return null

  return {
    temp:       Number(cur.temp_C),
    feels_like: Number(cur.FeelsLikeC),
    humidity:   Number(cur.humidity),
    condition:  cur.lang_zh?.[0]?.value || cur.weatherDesc?.[0]?.value || '',
    wind_kmh:   Number(cur.windspeedKmph),
    wind_dir:   cur.winddir16Point || '',
    uv_index:   Number(cur.uvIndex),
    visibility: Number(cur.visibility),
    today:      parseWttrDay(data.weather?.[0]),
    tomorrow:   parseWttrDay(data.weather?.[1]),
    fetched_at: new Date().toISOString(),
  }
}

// ─── 核心：采集 + 落盘 ────────────────────────────────────────────────────────

export async function collectGeoWeather() {
  // 读取已有缓存
  let stored = null
  if (fs.existsSync(GEO_WEATHER_FILE)) {
    stored = safe(() => JSON.parse(fs.readFileSync(GEO_WEATHER_FILE, 'utf8')))
  }

  let location = (stored?.version === GEO_WEATHER_VERSION) ? (stored.location || null) : null

  // 先轻量查一次当前公网 IP（复用 ip-api 调用，不做完整定位）
  const ipCheck = await fetchJSON(
    'http://ip-api.com/json/?fields=status,query',
    {},
    5000
  )
  const currentIP = (ipCheck?.status === 'success') ? ipCheck.query : null

  // 触发重查的两个条件：IP 变更（网络切换 / 出差），或缓存超过 7 天
  const cachedIP = location?.ip || null
  const locationAge = location?.collected_at
    ? Date.now() - new Date(location.collected_at).getTime()
    : Infinity
  const ipChanged  = currentIP && cachedIP && currentIP !== cachedIP
  const tooOld     = locationAge > LOCATION_REFRESH_MS

  if (ipChanged || tooOld || !location) {
    const reason = !location ? '首次采集' : ipChanged ? ('IP 变更 ' + cachedIP + ' → ' + currentIP) : '缓存超 7 天'
    console.log('[geo-weather] 刷新位置信息（' + reason + ')...')
    const fresh = await fetchLocation()
    if (fresh) {
      location = fresh
    } else if (location) {
      console.warn('[geo-weather] 位置刷新失败，沿用缓存')
    }
  } else {
    console.log('[geo-weather] 位置缓存有效（IP 未变），跳过重新定位')
  }

  // 天气：每次启动都刷新
  let weather = null
  if (location) {
    console.log('[geo-weather] 获取天气数据...')
    weather = await fetchWeather(location.lat, location.lon, location.city)
  }

  const result = {
    version:  GEO_WEATHER_VERSION,
    location: location || null,
    weather:  weather  || null,
  }

  safe(() => fs.writeFileSync(GEO_WEATHER_FILE, JSON.stringify(result, null, 2), 'utf8'))
  _cached = result

  const locLabel = location ? (location.city + ' (' + location.lat + ', ' + location.lon + ')') : '未知'
  const wxLabel  = weather  ? (weather.condition + ' ' + weather.temp + '°C') : '获取失败'
  console.log('[geo-weather] 完成 — 位置:', locLabel, '| 天气:', wxLabel)
  return result
}

// ─── 对外接口 ─────────────────────────────────────────────────────────────────

/**
 * 返回注入 system prompt 的纯文本块。
 * 必须在 collectGeoWeather() 完成后调用。
 */
export function getGeoWeatherBlock() {
  if (!_cached) return ''
  const parts = []

  // —— 位置块 ——
  const loc = _cached.location
  if (loc) {
    const lines = ['## Location & Coordinates']
    lines.push('Coordinates: ' + loc.lat + ', ' + loc.lon)

    const place = [loc.district, loc.city, loc.region, loc.country].filter(Boolean).join(', ')
    if (place) lines.push('Address: ' + place)
    if (loc.country_code) lines.push('Country Code: ' + loc.country_code)
    if (loc.timezone)     lines.push('Timezone: ' + loc.timezone)
    if (loc.ip)           lines.push('Public IP: ' + loc.ip + (loc.isp ? ' · ' + loc.isp : ''))
    if (loc.display_name) lines.push('Full Address: ' + loc.display_name)

    parts.push(lines.join('\n'))
  }

  // —— 天气块 ——
  const w = _cached.weather
  if (w) {
    const lines = ['## Current Weather']
    lines.push(
      'Now: ' + w.condition +
      ' · ' + w.temp + '°C (feels like ' + w.feels_like + '°C)' +
      ' · Humidity ' + w.humidity + '%'
    )
    lines.push(
      'Wind: ' + w.wind_kmh + ' km/h ' + w.wind_dir +
      ' · UV Index ' + w.uv_index +
      ' · Visibility ' + w.visibility + ' km'
    )

    if (w.today) {
      let todayLine = 'Today (' + w.today.date + '): ' + w.today.condition +
        ' · High ' + w.today.high + '°C / Low ' + w.today.low + '°C'
      if (w.today.sunrise) todayLine += ' · Sunrise ' + w.today.sunrise + ' / Sunset ' + w.today.sunset
      lines.push(todayLine)
    }

    if (w.tomorrow) {
      lines.push(
        'Tomorrow (' + w.tomorrow.date + '): ' + w.tomorrow.condition +
        ' · High ' + w.tomorrow.high + '°C / Low ' + w.tomorrow.low + '°C'
      )
    }

    parts.push(lines.join('\n'))
  }

  return parts.join('\n\n')
}
