/**
 * 날씨 API 서비스
 *
 * 기상청 단기예보 API를 사용하여 현재 날씨 정보를 조회
 * 날씨 조건에 따른 안전 체크리스트 필터링 지원
 *
 * @since 2026-02-02
 */

const axios = require('axios');
const logger = require('../utils/logger');
const { getDb } = require('../dbPool');

// 기상청 API 설정
const WEATHER_BASE_URL = process.env.WEATHER_API_URL || 'https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0';
const WEATHER_API = {
  baseUrl: WEATHER_BASE_URL,
  ultraShortUrl: `${WEATHER_BASE_URL}/getUltraSrtNcst`,
  shortForecastUrl: `${WEATHER_BASE_URL}/getVilageFcst`,
  apiKey: process.env.WEATHER_API_KEY || '',
  // 화성시 남양읍 좌표 (격자 좌표)
  // 위도: 37.2072, 경도: 126.8232
  defaultLocation: {
    nx: 57,  // 화성시 남양읍 X 좌표
    ny: 119  // 화성시 남양읍 Y 좌표
  }
};

// PTY (강수형태) 코드
const PTY_CODES = {
  0: 'none',     // 없음
  1: 'rain',     // 비
  2: 'rain',     // 비/눈 (혼합)
  3: 'snow',     // 눈
  4: 'rain',     // 소나기
  5: 'rain',     // 빗방울
  6: 'rain',     // 빗방울/눈날림
  7: 'snow'      // 눈날림
};

// SKY (하늘상태) 코드
const SKY_CODES = {
  1: 'clear',    // 맑음
  3: 'cloudy',   // 구름많음
  4: 'overcast'  // 흐림
};

/**
 * 현재 날씨 정보 조회 (초단기실황)
 * @param {number} nx - 격자 X 좌표 (optional)
 * @param {number} ny - 격자 Y 좌표 (optional)
 * @returns {Promise<Object>} 날씨 데이터
 */
async function getCurrentWeather(nx = WEATHER_API.defaultLocation.nx, ny = WEATHER_API.defaultLocation.ny) {
  if (!WEATHER_API.apiKey) {
    logger.warn('날씨 API 키가 설정되지 않음. 기본값 반환');
    return getDefaultWeatherData();
  }

  try {
    // 한국 시간 기준으로 base_date, base_time 계산
    const { baseDate, baseTime } = getBaseDateTime();

    logger.info('날씨 API 호출', { baseDate, baseTime, nx, ny });

    // Encoding 키는 이미 URL 인코딩되어 있으므로 직접 URL에 추가 (이중 인코딩 방지)
    const url = `${WEATHER_API.ultraShortUrl}?serviceKey=${WEATHER_API.apiKey}` +
      `&pageNo=1&numOfRows=10&dataType=JSON` +
      `&base_date=${baseDate}&base_time=${baseTime}` +
      `&nx=${nx}&ny=${ny}`;

    const response = await axios.get(url, { timeout: 5000 });

    if (response.data?.response?.header?.resultCode !== '00') {
      throw new Error(`API 오류: ${response.data?.response?.header?.resultMsg}`);
    }

    const items = response.data.response.body.items.item;
    const weatherData = parseWeatherItems(items);

    logger.info('날씨 데이터 파싱 완료', weatherData);

    return weatherData;
  } catch (error) {
    logger.error('날씨 API 호출 실패', { error: error.message });
    return getDefaultWeatherData();
  }
}

/**
 * 날씨 API 응답 파싱
 */
function parseWeatherItems(items) {
  const data = {
    temperature: null,
    humidity: null,
    windSpeed: null,
    precipitation: null,
    precipitationType: null,
    skyCondition: null
  };

  if (!items || !Array.isArray(items)) {
    return data;
  }

  items.forEach(item => {
    switch (item.category) {
      case 'T1H':  // 기온
        data.temperature = parseFloat(item.obsrValue);
        break;
      case 'REH':  // 습도
        data.humidity = parseInt(item.obsrValue);
        break;
      case 'WSD':  // 풍속
        data.windSpeed = parseFloat(item.obsrValue);
        break;
      case 'RN1':  // 1시간 강수량
        data.precipitation = parseFloat(item.obsrValue) || 0;
        break;
      case 'PTY':  // 강수형태
        data.precipitationType = parseInt(item.obsrValue);
        break;
    }
  });

  return data;
}

/**
 * 날씨 데이터를 기반으로 조건 판단
 * @param {Object} weatherData - 날씨 데이터
 * @returns {Promise<string[]>} 해당하는 날씨 조건 코드 배열
 */
async function determineWeatherConditions(weatherData) {
  const conditions = [];

  // DB에서 날씨 조건 기준 조회
  const db = await getDb();
  const [thresholds] = await db.execute(`
    SELECT condition_code, temp_threshold_min, temp_threshold_max,
           wind_threshold, precip_threshold
    FROM weather_conditions
    WHERE is_active = TRUE
  `);

  // 조건 판단
  thresholds.forEach(threshold => {
    let matches = false;

    switch (threshold.condition_code) {
      case 'rain':
        // 강수형태가 비(1,2,4,5,6) 또는 강수량 > 0
        if (weatherData.precipitationType && PTY_CODES[weatherData.precipitationType] === 'rain') {
          matches = true;
        } else if (weatherData.precipitation > 0 && threshold.precip_threshold !== null) {
          matches = weatherData.precipitation >= threshold.precip_threshold;
        }
        break;

      case 'snow':
        // 강수형태가 눈(3,7)
        if (weatherData.precipitationType && PTY_CODES[weatherData.precipitationType] === 'snow') {
          matches = true;
        }
        break;

      case 'heat':
        // 기온이 폭염 기준 이상
        if (weatherData.temperature !== null && threshold.temp_threshold_min !== null) {
          matches = weatherData.temperature >= threshold.temp_threshold_min;
        }
        break;

      case 'cold':
        // 기온이 한파 기준 이하
        if (weatherData.temperature !== null && threshold.temp_threshold_max !== null) {
          matches = weatherData.temperature <= threshold.temp_threshold_max;
        }
        break;

      case 'wind':
        // 풍속이 강풍 기준 이상
        if (weatherData.windSpeed !== null && threshold.wind_threshold !== null) {
          matches = weatherData.windSpeed >= threshold.wind_threshold;
        }
        break;

      case 'clear':
        // 강수 없고 기온이 정상 범위
        if (!weatherData.precipitationType || weatherData.precipitationType === 0) {
          if (weatherData.temperature !== null &&
              weatherData.temperature > -10 && weatherData.temperature < 35) {
            matches = true;
          }
        }
        break;
    }

    if (matches) {
      conditions.push(threshold.condition_code);
    }
  });

  // 조건이 없으면 기본으로 'clear' 추가
  if (conditions.length === 0) {
    conditions.push('clear');
  }

  logger.info('날씨 조건 판단 완료', { weatherData, conditions });

  return conditions;
}

/**
 * TBM 세션에 날씨 정보 저장
 * @param {number} sessionId - TBM 세션 ID
 * @param {Object} weatherData - 날씨 데이터
 * @param {string[]} conditions - 날씨 조건 배열
 */
async function saveWeatherRecord(sessionId, weatherData, conditions) {
  const db = await getDb();

  try {
    const weatherDate = new Date().toISOString().split('T')[0];

    await db.execute(`
      INSERT INTO tbm_weather_records
        (session_id, weather_date, temperature, humidity, wind_speed, precipitation,
         weather_condition, weather_conditions, data_source, fetched_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'api', NOW())
      ON DUPLICATE KEY UPDATE
        temperature = VALUES(temperature),
        humidity = VALUES(humidity),
        wind_speed = VALUES(wind_speed),
        precipitation = VALUES(precipitation),
        weather_condition = VALUES(weather_condition),
        weather_conditions = VALUES(weather_conditions),
        fetched_at = NOW()
    `, [
      sessionId,
      weatherDate,
      weatherData.temperature,
      weatherData.humidity,
      weatherData.windSpeed,
      weatherData.precipitation,
      conditions[0] || 'clear',  // 주요 조건
      JSON.stringify(conditions) // 모든 조건
    ]);

    logger.info('날씨 기록 저장 완료', { sessionId, conditions });
    return { success: true };
  } catch (error) {
    logger.error('날씨 기록 저장 실패', { sessionId, error: error.message });
    throw error;
  }
}

/**
 * TBM 세션의 날씨 기록 조회
 * @param {number} sessionId - TBM 세션 ID
 */
async function getWeatherRecord(sessionId) {
  const db = await getDb();

  const [rows] = await db.execute(`
    SELECT wr.*, wc.condition_name, wc.icon
    FROM tbm_weather_records wr
    LEFT JOIN weather_conditions wc ON wr.weather_condition = wc.condition_code
    WHERE wr.session_id = ?
  `, [sessionId]);

  if (rows.length === 0) {
    return null;
  }

  const record = rows[0];
  // JSON 문자열 파싱
  if (record.weather_conditions && typeof record.weather_conditions === 'string') {
    try {
      record.weather_conditions = JSON.parse(record.weather_conditions);
    } catch (e) {
      record.weather_conditions = [];
    }
  }

  return record;
}

/**
 * 날씨 조건 코드 목록 조회
 */
async function getWeatherConditionList() {
  const db = await getDb();

  const [rows] = await db.execute(`
    SELECT condition_code, condition_name, description, icon,
           temp_threshold_min, temp_threshold_max, wind_threshold, precip_threshold
    FROM weather_conditions
    WHERE is_active = TRUE
    ORDER BY display_order
  `);

  return rows;
}

/**
 * 기본 날씨 데이터 반환 (API 실패 시)
 */
function getDefaultWeatherData() {
  return {
    temperature: 20,
    humidity: 50,
    windSpeed: 2,
    precipitation: 0,
    precipitationType: 0,
    skyCondition: 'clear',
    isDefault: true
  };
}

/**
 * 기상청 API용 기준 날짜/시간 계산
 * 한국 시간(KST, UTC+9) 기준으로 계산
 * 매시간 정각에 생성되고 10분 후에 제공됨
 * @returns {{ baseDate: string, baseTime: string }}
 */
function getBaseDateTime() {
  // 한국 시간으로 변환 (UTC + 9시간)
  const now = new Date();
  const kstOffset = 9 * 60 * 60 * 1000; // 9시간을 밀리초로
  const kstDate = new Date(now.getTime() + kstOffset);

  let year = kstDate.getUTCFullYear();
  let month = kstDate.getUTCMonth() + 1;
  let day = kstDate.getUTCDate();
  let hours = kstDate.getUTCHours();
  let minutes = kstDate.getUTCMinutes();

  // 10분 이전이면 이전 시간 데이터 사용
  if (minutes < 10) {
    hours = hours - 1;
    if (hours < 0) {
      hours = 23;
      // 전날로 변경
      const prevDay = new Date(kstDate.getTime() - 24 * 60 * 60 * 1000);
      year = prevDay.getUTCFullYear();
      month = prevDay.getUTCMonth() + 1;
      day = prevDay.getUTCDate();
    }
  }

  const baseDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`;
  const baseTime = String(hours).padStart(2, '0') + '00';

  return { baseDate, baseTime };
}

/**
 * 날짜 포맷 (YYYYMMDD) - 호환성 유지용
 * @deprecated getBaseDateTime() 사용 권장
 */
function formatDate(date) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  return `${year}${month}${day}`;
}

/**
 * @deprecated getBaseDateTime() 사용 권장
 */
function getBaseTime(date) {
  let hours = date.getHours();
  let minutes = date.getMinutes();

  if (minutes < 10) {
    hours = hours - 1;
    if (hours < 0) hours = 23;
  }

  return String(hours).padStart(2, '0') + '00';
}

/**
 * 위경도를 기상청 격자 좌표로 변환
 * LCC (Lambert Conformal Conic) 투영법 사용
 */
function convertToGrid(lat, lon) {
  const RE = 6371.00877;  // 지구 반경(km)
  const GRID = 5.0;       // 격자 간격(km)
  const SLAT1 = 30.0;     // 투영 위도1(degree)
  const SLAT2 = 60.0;     // 투영 위도2(degree)
  const OLON = 126.0;     // 기준점 경도(degree)
  const OLAT = 38.0;      // 기준점 위도(degree)
  const XO = 43;          // 기준점 X좌표(GRID)
  const YO = 136;         // 기준점 Y좌표(GRID)

  const DEGRAD = Math.PI / 180.0;

  const re = RE / GRID;
  const slat1 = SLAT1 * DEGRAD;
  const slat2 = SLAT2 * DEGRAD;
  const olon = OLON * DEGRAD;
  const olat = OLAT * DEGRAD;

  let sn = Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5);
  sn = Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(sn);
  let sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
  sf = Math.pow(sf, sn) * Math.cos(slat1) / sn;
  let ro = Math.tan(Math.PI * 0.25 + olat * 0.5);
  ro = re * sf / Math.pow(ro, sn);

  let ra = Math.tan(Math.PI * 0.25 + lat * DEGRAD * 0.5);
  ra = re * sf / Math.pow(ra, sn);
  let theta = lon * DEGRAD - olon;
  if (theta > Math.PI) theta -= 2.0 * Math.PI;
  if (theta < -Math.PI) theta += 2.0 * Math.PI;
  theta *= sn;

  const x = Math.floor(ra * Math.sin(theta) + XO + 0.5);
  const y = Math.floor(ro - ra * Math.cos(theta) + YO + 0.5);

  return { nx: x, ny: y };
}

module.exports = {
  getCurrentWeather,
  determineWeatherConditions,
  saveWeatherRecord,
  getWeatherRecord,
  getWeatherConditionList,
  convertToGrid,
  getDefaultWeatherData
};
