/**
 * OpenAPI 프록시 컨트롤러
 * - 외부 API(날씨, 환율 등)를 프록시하여 API 키를 안전하게 관리
 */

import { Request, Response } from 'express';
import axios from 'axios';

export class OpenApiProxyController {
  /**
   * 날씨 정보 조회 (기상청 API Hub)
   * GET /api/open-api/weather?city=Seoul
   */
  async getWeather(req: Request, res: Response): Promise<void> {
    try {
      const { city = '서울' } = req.query;

      console.log(`🌤️ 날씨 조회 요청: ${city}`);

      // 1순위: OpenWeatherMap API (실시간에 가까움, 10분마다 업데이트)
      const openWeatherKey = process.env.OPENWEATHER_API_KEY;
      if (openWeatherKey) {
        try {
          console.log(`🌍 OpenWeatherMap API 호출: ${city}`);
          const response = await axios.get('https://api.openweathermap.org/data/2.5/weather', {
            params: {
              q: `${city},KR`,
              appid: openWeatherKey,
              units: 'metric',
              lang: 'kr',
            },
            timeout: 10000,
          });

          const data = response.data;
          const weatherData = {
            city: data.name,
            country: data.sys.country,
            temperature: Math.round(data.main.temp),
            feelsLike: Math.round(data.main.feels_like),
            humidity: data.main.humidity,
            pressure: data.main.pressure,
            weatherMain: data.weather[0].main,
            weatherDescription: data.weather[0].description,
            weatherIcon: data.weather[0].icon,
            windSpeed: Math.round(data.wind.speed * 10) / 10,
            clouds: data.clouds.all,
            timestamp: new Date().toISOString(),
          };

          console.log(`✅ OpenWeatherMap 날씨 조회 성공: ${weatherData.city} ${weatherData.temperature}°C`);
          res.json({ success: true, data: weatherData });
          return;
        } catch (error) {
          console.warn('⚠️ OpenWeatherMap API 실패, 기상청 API로 폴백:', error instanceof Error ? error.message : error);
        }
      }

      // 2순위: 기상청 API Hub (매시간 정시 데이터)
      const apiKey = process.env.KMA_API_KEY;
      
      // API 키가 없으면 오류 반환
      if (!apiKey) {
        console.log('⚠️ 기상청 API 키가 설정되지 않았습니다.');
        res.status(503).json({
          success: false,
          message: '기상청 API 키가 설정되지 않았습니다. 관리자에게 문의하세요.',
        });
        return;
      }

      // 도시명 → 기상청 지역 코드 매핑
      const regionCode = getKMARegionCode(city as string);
      
      if (!regionCode) {
        res.status(404).json({
          success: false,
          message: `지원하지 않는 지역입니다: ${city}`,
        });
        return;
      }

      // 기상청 API Hub 사용 (apihub.kma.go.kr)
      const now = new Date();
      
      // 한국 시간(KST = UTC+9)으로 변환
      const kstOffset = 9 * 60 * 60 * 1000; // 9시간을 밀리초로
      const kstNow = new Date(now.getTime() + kstOffset);
      
      // 기상청 지상관측 데이터는 매시간 정시(XX:00)에 발표
      // 가장 최근의 정시 데이터를 가져오기 위해 현재 시간의 정시로 설정
      const targetTime = new Date(kstNow);
      
      // tm 파라미터: YYYYMMDDHH00 형식 (정시만 조회)
      const year = targetTime.getUTCFullYear();
      const month = String(targetTime.getUTCMonth() + 1).padStart(2, '0');
      const day = String(targetTime.getUTCDate()).padStart(2, '0');
      const hour = String(targetTime.getUTCHours()).padStart(2, '0');
      const tm = `${year}${month}${day}${hour}00`;
      
      console.log(`🕐 현재 시각(KST): ${kstNow.toISOString().slice(0, 16).replace('T', ' ')}, 조회 시각: ${tm}`);

      // 기상청 API Hub - 지상관측시간자료 (시간 범위 조회로 최신 데이터 확보)
      // sfctm3: 시간 범위 조회 가능 (tm1~tm2)
      const url = 'https://apihub.kma.go.kr/api/typ01/url/kma_sfctm3.php';
      
      // 최근 1시간 범위 조회 (현재 시간 - 1시간 ~ 현재 시간) - KST 기준
      const tm1Time = new Date(kstNow.getTime() - 60 * 60 * 1000); // 1시간 전
      const tm1 = `${tm1Time.getUTCFullYear()}${String(tm1Time.getUTCMonth() + 1).padStart(2, '0')}${String(tm1Time.getUTCDate()).padStart(2, '0')}${String(tm1Time.getUTCHours()).padStart(2, '0')}00`;
      const tm2 = tm; // 현재 시간

      console.log(`📡 기상청 API Hub 호출: ${regionCode.name} (관측소: ${regionCode.stnId}, 기간: ${tm1}~${tm2})`);

      const response = await axios.get(url, {
        params: {
          tm1: tm1,
          tm2: tm2,
          stn: regionCode.stnId, // 특정 관측소만 조회
          authKey: apiKey,
          help: 0,
          disp: 1,
        },
        timeout: 10000,
      });

      console.log('📊 기상청 API Hub 응답:', response.data);

      // 기상청 API Hub 응답은 텍스트 형식이므로 파싱 필요
      const weatherData = parseKMAHubWeatherData(response.data, regionCode);

      console.log(`✅ 날씨 조회 성공: ${weatherData.city} ${weatherData.temperature}°C`);

      res.json({
        success: true,
        data: weatherData,
      });
    } catch (error: unknown) {
      console.error('❌ 날씨 조회 실패:', error);

      // API 호출 실패 시 명확한 오류 메시지 반환
      if (axios.isAxiosError(error)) {
        const status = error.response?.status;
        
        if (status === 401 || status === 403) {
          res.status(401).json({
            success: false,
            message: '기상청 API 인증에 실패했습니다. API 키를 확인하세요.',
          });
        } else if (status === 404) {
          res.status(404).json({
            success: false,
            message: '기상청 API에서 데이터를 찾을 수 없습니다.',
          });
        } else if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') {
          res.status(504).json({
            success: false,
            message: '기상청 API 연결 시간이 초과되었습니다. 잠시 후 다시 시도하세요.',
          });
        } else {
          res.status(500).json({
            success: false,
            message: '기상청 API 호출 중 오류가 발생했습니다.',
            error: error.message,
          });
        }
      } else {
        res.status(500).json({
          success: false,
          message: '날씨 정보를 가져오는 중 예상치 못한 오류가 발생했습니다.',
        });
      }
    }
  }

  /**
   * 환율 정보 조회 (한국은행 API)
   * GET /api/open-api/exchange-rate?base=KRW&target=USD
   */
  async getExchangeRate(req: Request, res: Response): Promise<void> {
    try {
      const { base = 'KRW', target = 'USD' } = req.query;

      console.log(`💱 환율 조회 요청: ${base} -> ${target}`);

      // ExchangeRate-API.com 사용 (무료, API 키 불필요)
      const url = `https://open.er-api.com/v6/latest/${base}`;

      console.log(`📡 ExchangeRate-API 호출: ${base} -> ${target}`);

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

      console.log('📊 ExchangeRate-API 응답:', response.data);

      // 환율 데이터 추출
      const rates = response.data?.rates;
      if (!rates || !rates[target as string]) {
        throw new Error(`환율 데이터를 찾을 수 없습니다: ${base} -> ${target}`);
      }

      const rate = rates[target as string];
      const exchangeData = {
        base: base as string,
        target: target as string,
        rate: rate,
        timestamp: new Date().toISOString(),
        source: 'ExchangeRate-API.com',
      };

      console.log(`✅ 환율 조회 성공: 1 ${base} = ${rate} ${target}`);

      res.json({
        success: true,
        data: exchangeData,
      });
    } catch (error: unknown) {
      console.error('❌ 환율 조회 실패:', error);

      // API 호출 실패 시 명확한 오류 메시지 반환
      if (axios.isAxiosError(error)) {
        res.status(500).json({
          success: false,
          message: '환율 정보를 가져오는 중 오류가 발생했습니다.',
          error: error.message,
        });
      } else {
        res.status(500).json({
          success: false,
          message: '환율 정보를 가져오는 중 예상치 못한 오류가 발생했습니다.',
        });
      }
    }
  }

  /**
   * Geocoding (주소 → 좌표 변환)
   * POST /api/open-api/geocode
   * Body: { address: "서울특별시 강남구 테헤란로 123" }
   */
  async geocode(req: Request, res: Response): Promise<void> {
    try {
      const { address } = req.body;

      if (!address) {
        res.status(400).json({
          success: false,
          message: '주소가 필요합니다.',
        });
        return;
      }

      console.log(`📍 Geocoding 요청: ${address}`);

      // Kakao Geocoding API 호출
      const apiKey = process.env.KAKAO_REST_API_KEY || 'demo'; // TODO: 실제 API 키 필요
      const url = 'https://dapi.kakao.com/v2/local/search/address.json';

      const response = await axios.get(url, {
        params: { query: address },
        headers: {
          Authorization: `KakaoAK ${apiKey}`,
        },
        timeout: 5000,
      });

      if (response.data.documents && response.data.documents.length > 0) {
        const result = response.data.documents[0];
        console.log(`✅ Geocoding 성공: ${result.address_name}`);

        res.json({
          success: true,
          data: {
            address: result.address_name,
            lat: parseFloat(result.y),
            lng: parseFloat(result.x),
          },
        });
      } else {
        console.log(`❌ 주소를 찾을 수 없음: ${address}`);
        res.status(404).json({
          success: false,
          message: '주소를 찾을 수 없습니다.',
        });
      }
    } catch (error: unknown) {
      console.error('❌ Geocoding 실패:', error);

      if (axios.isAxiosError(error)) {
        res.status(500).json({
          success: false,
          message: 'Geocoding 중 오류가 발생했습니다.',
          error: error.message,
        });
      } else {
        res.status(500).json({
          success: false,
          message: 'Geocoding 중 오류가 발생했습니다.',
        });
      }
    }
  }
}

/**
 * 기상청 지역 코드 매핑 (전국 시/군/구 단위)
 */
function getKMARegionCode(city: string): { name: string; stnId: string } | null {
  const regions: Record<string, { name: string; stnId: string }> = {
    // 영어 도시명 (국제 표준)
    'Seoul': { name: '서울', stnId: '108' },
    'Busan': { name: '부산', stnId: '159' },
    'Incheon': { name: '인천', stnId: '112' },
    'Daegu': { name: '대구', stnId: '143' },
    'Gwangju': { name: '광주', stnId: '156' },
    'Daejeon': { name: '대전', stnId: '133' },
    'Ulsan': { name: '울산', stnId: '152' },
    'Sejong': { name: '세종', stnId: '239' },
    'Jeju': { name: '제주', stnId: '184' },
    
    // 서울특별시
    '서울': { name: '서울', stnId: '108' },
    '종로구': { name: '서울 종로구', stnId: '108' },
    '중구': { name: '서울 중구', stnId: '108' },
    '용산구': { name: '서울 용산구', stnId: '108' },
    '성동구': { name: '서울 성동구', stnId: '108' },
    '광진구': { name: '서울 광진구', stnId: '108' },
    '동대문구': { name: '서울 동대문구', stnId: '108' },
    '중랑구': { name: '서울 중랑구', stnId: '108' },
    '성북구': { name: '서울 성북구', stnId: '108' },
    '강북구': { name: '서울 강북구', stnId: '108' },
    '도봉구': { name: '서울 도봉구', stnId: '108' },
    '노원구': { name: '서울 노원구', stnId: '108' },
    '은평구': { name: '서울 은평구', stnId: '108' },
    '서대문구': { name: '서울 서대문구', stnId: '108' },
    '마포구': { name: '서울 마포구', stnId: '108' },
    '양천구': { name: '서울 양천구', stnId: '108' },
    '강서구': { name: '서울 강서구', stnId: '108' },
    '구로구': { name: '서울 구로구', stnId: '108' },
    '금천구': { name: '서울 금천구', stnId: '108' },
    '영등포구': { name: '서울 영등포구', stnId: '108' },
    '동작구': { name: '서울 동작구', stnId: '108' },
    '관악구': { name: '서울 관악구', stnId: '108' },
    '서초구': { name: '서울 서초구', stnId: '108' },
    '강남구': { name: '서울 강남구', stnId: '108' },
    '송파구': { name: '서울 송파구', stnId: '108' },
    '강동구': { name: '서울 강동구', stnId: '108' },
    
    // 부산광역시
    '부산': { name: '부산', stnId: '159' },
    '중구(부산)': { name: '부산 중구', stnId: '159' },
    '서구(부산)': { name: '부산 서구', stnId: '159' },
    '동구(부산)': { name: '부산 동구', stnId: '159' },
    '영도구': { name: '부산 영도구', stnId: '159' },
    '부산진구': { name: '부산 부산진구', stnId: '159' },
    '동래구': { name: '부산 동래구', stnId: '159' },
    '남구(부산)': { name: '부산 남구', stnId: '159' },
    '북구(부산)': { name: '부산 북구', stnId: '159' },
    '해운대구': { name: '부산 해운대구', stnId: '159' },
    '사하구': { name: '부산 사하구', stnId: '159' },
    '금정구': { name: '부산 금정구', stnId: '159' },
    '강서구(부산)': { name: '부산 강서구', stnId: '159' },
    '연제구': { name: '부산 연제구', stnId: '159' },
    '수영구': { name: '부산 수영구', stnId: '159' },
    '사상구': { name: '부산 사상구', stnId: '159' },
    '기장군': { name: '부산 기장군', stnId: '159' },
    
    // 인천광역시
    '인천': { name: '인천', stnId: '112' },
    '중구(인천)': { name: '인천 중구', stnId: '112' },
    '동구(인천)': { name: '인천 동구', stnId: '112' },
    '미추홀구': { name: '인천 미추홀구', stnId: '112' },
    '연수구': { name: '인천 연수구', stnId: '112' },
    '남동구': { name: '인천 남동구', stnId: '112' },
    '부평구': { name: '인천 부평구', stnId: '112' },
    '계양구': { name: '인천 계양구', stnId: '112' },
    '서구(인천)': { name: '인천 서구', stnId: '112' },
    '강화군': { name: '인천 강화군', stnId: '201' },
    '옹진군': { name: '인천 옹진군', stnId: '112' },
    
    // 대구광역시
    '대구': { name: '대구', stnId: '143' },
    '중구(대구)': { name: '대구 중구', stnId: '143' },
    '동구(대구)': { name: '대구 동구', stnId: '143' },
    '서구(대구)': { name: '대구 서구', stnId: '143' },
    '남구(대구)': { name: '대구 남구', stnId: '143' },
    '북구(대구)': { name: '대구 북구', stnId: '143' },
    '수성구': { name: '대구 수성구', stnId: '143' },
    '달서구': { name: '대구 달서구', stnId: '143' },
    '달성군': { name: '대구 달성군', stnId: '143' },
    
    // 광주광역시
    '광주': { name: '광주', stnId: '156' },
    '동구(광주)': { name: '광주 동구', stnId: '156' },
    '서구(광주)': { name: '광주 서구', stnId: '156' },
    '남구(광주)': { name: '광주 남구', stnId: '156' },
    '북구(광주)': { name: '광주 북구', stnId: '156' },
    '광산구': { name: '광주 광산구', stnId: '156' },
    
    // 대전광역시
    '대전': { name: '대전', stnId: '133' },
    '동구(대전)': { name: '대전 동구', stnId: '133' },
    '중구(대전)': { name: '대전 중구', stnId: '133' },
    '서구(대전)': { name: '대전 서구', stnId: '133' },
    '유성구': { name: '대전 유성구', stnId: '133' },
    '대덕구': { name: '대전 대덕구', stnId: '133' },
    
    // 울산광역시
    '울산': { name: '울산', stnId: '152' },
    '중구(울산)': { name: '울산 중구', stnId: '152' },
    '남구(울산)': { name: '울산 남구', stnId: '152' },
    '동구(울산)': { name: '울산 동구', stnId: '152' },
    '북구(울산)': { name: '울산 북구', stnId: '152' },
    '울주군': { name: '울산 울주군', stnId: '152' },
    
    // 세종특별자치시
    '세종': { name: '세종', stnId: '239' },
    
    // 경기도
    '수원': { name: '수원', stnId: '119' },
    '성남': { name: '성남', stnId: '119' },
    '의정부': { name: '의정부', stnId: '108' },
    '안양': { name: '안양', stnId: '119' },
    '부천': { name: '부천', stnId: '112' },
    '광명': { name: '광명', stnId: '119' },
    '평택': { name: '평택', stnId: '119' },
    '동두천': { name: '동두천', stnId: '98' },
    '안산': { name: '안산', stnId: '119' },
    '고양': { name: '고양', stnId: '108' },
    '과천': { name: '과천', stnId: '119' },
    '구리': { name: '구리', stnId: '108' },
    '남양주': { name: '남양주', stnId: '108' },
    '오산': { name: '오산', stnId: '119' },
    '시흥': { name: '시흥', stnId: '119' },
    '군포': { name: '군포', stnId: '119' },
    '의왕': { name: '의왕', stnId: '119' },
    '하남': { name: '하남', stnId: '108' },
    '용인': { name: '용인', stnId: '119' },
    '파주': { name: '파주', stnId: '98' },
    '이천': { name: '이천', stnId: '203' },
    '안성': { name: '안성', stnId: '119' },
    '김포': { name: '김포', stnId: '112' },
    '화성': { name: '화성', stnId: '119' },
    '광주(경기)': { name: '광주(경기)', stnId: '119' },
    '양주': { name: '양주', stnId: '98' },
    '포천': { name: '포천', stnId: '98' },
    '여주': { name: '여주', stnId: '203' },
    '연천군': { name: '연천', stnId: '98' },
    '가평군': { name: '가평', stnId: '201' },
    '양평군': { name: '양평', stnId: '119' },
    
    // 강원도
    '춘천': { name: '춘천', stnId: '101' },
    '원주': { name: '원주', stnId: '114' },
    '강릉': { name: '강릉', stnId: '105' },
    '동해': { name: '동해', stnId: '106' },
    '태백': { name: '태백', stnId: '216' },
    '속초': { name: '속초', stnId: '90' },
    '삼척': { name: '삼척', stnId: '106' },
    '홍천': { name: '홍천', stnId: '212' },
    '횡성': { name: '횡성', stnId: '114' },
    '영월': { name: '영월', stnId: '121' },
    '평창': { name: '평창', stnId: '100' },
    '정선': { name: '정선', stnId: '217' },
    '철원': { name: '철원', stnId: '95' },
    '화천': { name: '화천', stnId: '212' },
    '양구': { name: '양구', stnId: '212' },
    '인제': { name: '인제', stnId: '211' },
    '고성(강원)': { name: '고성(강원)', stnId: '90' },
    '양양': { name: '양양', stnId: '90' },
    
    // 충청북도
    '청주': { name: '청주', stnId: '131' },
    '충주': { name: '충주', stnId: '127' },
    '제천': { name: '제천', stnId: '221' },
    '보은': { name: '보은', stnId: '226' },
    '옥천': { name: '옥천', stnId: '226' },
    '영동': { name: '영동', stnId: '135' },
    '증평': { name: '증평', stnId: '131' },
    '진천': { name: '진천', stnId: '131' },
    '괴산': { name: '괴산', stnId: '127' },
    '음성': { name: '음성', stnId: '127' },
    '단양': { name: '단양', stnId: '221' },
    
    // 충청남도
    '천안': { name: '천안', stnId: '232' },
    '공주': { name: '공주', stnId: '236' },
    '보령': { name: '보령', stnId: '235' },
    '아산': { name: '아산', stnId: '232' },
    '서산': { name: '서산', stnId: '129' },
    '논산': { name: '논산', stnId: '238' },
    '계룡': { name: '계룡', stnId: '238' },
    '당진': { name: '당진', stnId: '129' },
    '금산': { name: '금산', stnId: '238' },
    '부여': { name: '부여', stnId: '236' },
    '서천': { name: '서천', stnId: '235' },
    '청양': { name: '청양', stnId: '235' },
    '홍성': { name: '홍성', stnId: '177' },
    '예산': { name: '예산', stnId: '232' },
    '태안': { name: '태안', stnId: '129' },
    
    // 전라북도
    '전주': { name: '전주', stnId: '146' },
    '군산': { name: '군산', stnId: '140' },
    '익산': { name: '익산', stnId: '146' },
    '정읍': { name: '정읍', stnId: '251' },
    '남원': { name: '남원', stnId: '247' },
    '김제': { name: '김제', stnId: '140' },
    '완주': { name: '완주', stnId: '146' },
    '진안': { name: '진안', stnId: '248' },
    '무주': { name: '무주', stnId: '248' },
    '장수': { name: '장수', stnId: '248' },
    '임실': { name: '임실', stnId: '247' },
    '순창': { name: '순창', stnId: '254' },
    '고창': { name: '고창', stnId: '172' },
    '부안': { name: '부안', stnId: '140' },
    
    // 전라남도
    '목포': { name: '목포', stnId: '165' },
    '여수': { name: '여수', stnId: '168' },
    '순천': { name: '순천', stnId: '174' },
    '나주': { name: '나주', stnId: '170' },
    '광양': { name: '광양', stnId: '168' },
    '담양': { name: '담양', stnId: '156' },
    '곡성': { name: '곡성', stnId: '252' },
    '구례': { name: '구례', stnId: '252' },
    '고흥': { name: '고흥', stnId: '262' },
    '보성': { name: '보성', stnId: '258' },
    '화순': { name: '화순', stnId: '262' },
    '장흥': { name: '장흥', stnId: '260' },
    '강진': { name: '강진', stnId: '259' },
    '해남': { name: '해남', stnId: '261' },
    '영암': { name: '영암', stnId: '170' },
    '무안': { name: '무안', stnId: '165' },
    '함평': { name: '함평', stnId: '172' },
    '영광': { name: '영광', stnId: '252' },
    '장성': { name: '장성', stnId: '172' },
    '완도': { name: '완도', stnId: '261' },
    '진도': { name: '진도', stnId: '175' },
    '신안': { name: '신안', stnId: '165' },
    
    // 경상북도
    '포항': { name: '포항', stnId: '138' },
    '경주': { name: '경주', stnId: '283' },
    '김천': { name: '김천', stnId: '279' },
    '안동': { name: '안동', stnId: '136' },
    '구미': { name: '구미', stnId: '279' },
    '영주': { name: '영주', stnId: '272' },
    '영천': { name: '영천', stnId: '281' },
    '상주': { name: '상주', stnId: '137' },
    '문경': { name: '문경', stnId: '273' },
    '경산': { name: '경산', stnId: '283' },
    '군위': { name: '군위', stnId: '278' },
    '의성': { name: '의성', stnId: '278' },
    '청송': { name: '청송', stnId: '276' },
    '영양': { name: '영양', stnId: '277' },
    '영덕': { name: '영덕', stnId: '277' },
    '청도': { name: '청도', stnId: '284' },
    '고령': { name: '고령', stnId: '279' },
    '성주': { name: '성주', stnId: '279' },
    '칠곡': { name: '칠곡', stnId: '279' },
    '예천': { name: '예천', stnId: '273' },
    '봉화': { name: '봉화', stnId: '271' },
    '울진': { name: '울진', stnId: '130' },
    '울릉도': { name: '울릉도', stnId: '115' },
    '독도': { name: '독도', stnId: '115' },
    
    // 경상남도
    '창원': { name: '창원', stnId: '155' },
    '진주': { name: '진주', stnId: '192' },
    '통영': { name: '통영', stnId: '162' },
    '사천': { name: '사천', stnId: '192' },
    '김해': { name: '김해', stnId: '159' },
    '밀양': { name: '밀양', stnId: '288' },
    '거제': { name: '거제', stnId: '162' },
    '양산': { name: '양산', stnId: '288' },
    '의령': { name: '의령', stnId: '192' },
    '함안': { name: '함안', stnId: '155' },
    '창녕': { name: '창녕', stnId: '288' },
    '고성(경남)': { name: '고성(경남)', stnId: '162' },
    '남해': { name: '남해', stnId: '295' },
    '하동': { name: '하동', stnId: '295' },
    '산청': { name: '산청', stnId: '289' },
    '함양': { name: '함양', stnId: '289' },
    '거창': { name: '거창', stnId: '284' },
    '합천': { name: '합천', stnId: '285' },
    
    // 제주특별자치도
    '제주': { name: '제주', stnId: '184' },
    '서귀포': { name: '서귀포', stnId: '189' },
    '성산': { name: '제주 성산', stnId: '188' },
    '고산': { name: '제주 고산', stnId: '185' },
  };

  return regions[city] || null;
}

/**
 * 관측소 ID를 격자 좌표로 변환
 */
function convertStationToGrid(stnId: string): { nx: number; ny: number } {
  const gridMap: Record<string, { nx: number; ny: number }> = {
    '108': { nx: 60, ny: 127 }, // 서울
    '159': { nx: 98, ny: 76 },  // 부산
    '112': { nx: 55, ny: 124 }, // 인천
    '143': { nx: 89, ny: 90 },  // 대구
    '156': { nx: 58, ny: 74 },  // 광주
    '133': { nx: 67, ny: 100 }, // 대전
    '152': { nx: 102, ny: 84 }, // 울산
    '239': { nx: 66, ny: 103 }, // 세종
    '119': { nx: 60, ny: 121 }, // 수원
    '101': { nx: 73, ny: 134 }, // 춘천
    '184': { nx: 52, ny: 38 },  // 제주
    '105': { nx: 92, ny: 131 }, // 강릉
  };

  return gridMap[stnId] || { nx: 60, ny: 127 }; // 기본값: 서울
}

/**
 * 날짜 포맷 (YYYYMMDD)
 */
function formatDate(date: Date): string {
  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}`;
}

/**
 * 기상청 발표 시각 계산
 * 매시간 40분에 발표, 10분 후 조회 가능
 */
function getBaseTime(date: Date): string {
  const hour = date.getHours();
  const minute = date.getMinutes();
  
  // 현재 시각이 40분 이전이면 이전 시간 데이터 조회
  let baseHour = minute < 40 ? hour - 1 : hour;
  
  // 0시 이전이면 전날 23시
  if (baseHour < 0) {
    baseHour = 23;
  }
  
  return String(baseHour).padStart(2, '0') + '00';
}

/**
 * 기상청 API Hub 데이터 파싱
 */
function parseKMAHubWeatherData(data: any, regionCode: { name: string; stnId: string }): any {
  // API Hub 응답 형식: #을 구분자로 사용하는 텍스트
  const lines = data.split('\n').filter((line: string) => line.trim() && !line.startsWith('#'));
  
  if (lines.length === 0) {
    throw new Error('날씨 데이터를 파싱할 수 없습니다.');
  }

  // 요청한 관측소(stnId)의 모든 데이터 찾기 (시간 범위 조회 시 여러 줄 반환됨)
  const targetLines = lines.filter((line: string) => {
    const cols = line.trim().split(/\s+/);
    return cols[1] === regionCode.stnId; // STN 컬럼 (인덱스 1)
  });
  
  if (targetLines.length === 0) {
    throw new Error(`${regionCode.name} 관측소 데이터를 찾을 수 없습니다.`);
  }

  // 가장 최근 데이터 선택 (마지막 줄)
  const targetLine = targetLines[targetLines.length - 1];
  
  // 데이터 라인 파싱 (공백으로 구분)
  const values = targetLine.trim().split(/\s+/);
  
  // 관측 시각 로깅
  const obsTime = values[0]; // YYMMDDHHMI
  console.log(`🕐 관측 시각: ${obsTime} (${regionCode.name})`);
  
  // 기상청 API Hub 데이터 형식 (실제 응답 기준):
  // [0]YYMMDDHHMI [1]STN [2]WD [3]WS [4]GST_WD [5]GST_WS [6]GST_TM [7]PA [8]PS [9]PT [10]PR [11]TA [12]TD [13]HM [14]PV [15]RN ...
  const temperature = parseFloat(values[11]) || 0; // TA: 기온 (인덱스 11)
  const humidity = parseFloat(values[13]) || 0; // HM: 습도 (인덱스 13)
  const pressure = parseFloat(values[7]) || 1013; // PA: 현지기압 (인덱스 7)
  const windSpeed = parseFloat(values[3]) || 0; // WS: 풍속 (인덱스 3)
  const rainfall = parseFloat(values[15]) || 0; // RN: 강수량 (인덱스 15)

  console.log(`📊 기상 데이터: ${regionCode.name} - 기온 ${temperature}°C, 습도 ${humidity}%, 강수량 ${rainfall}mm`);

  // 날씨 상태 추정 (강수량 + 습도 기반)
  let weatherMain = 'Clear';
  let weatherDescription = '맑음';
  let weatherIcon = '01d';
  let clouds = 10;

  // 강수량이 있으면 비
  if (rainfall > 0) {
    if (rainfall >= 10) {
      weatherMain = 'Rain';
      weatherDescription = '비 (강수)';
      weatherIcon = '10d';
      clouds = 100;
    } else {
      weatherMain = 'Rain';
      weatherDescription = '비 (약간)';
      weatherIcon = '09d';
      clouds = 90;
    }
  } 
  // 습도 기반 날씨 추정
  else if (humidity > 80) {
    weatherMain = 'Clouds';
    weatherDescription = '흐림';
    weatherIcon = '04d';
    clouds = 90;
  } else if (humidity > 60) {
    weatherMain = 'Clouds';
    weatherDescription = '구름 많음';
    weatherIcon = '03d';
    clouds = 60;
  } else if (humidity > 40) {
    weatherMain = 'Clouds';
    weatherDescription = '구름 조금';
    weatherIcon = '02d';
    clouds = 30;
  }

  return {
    city: regionCode.name,
    country: 'KR',
    temperature: Math.round(temperature),
    feelsLike: Math.round(temperature - 2),
    humidity: Math.round(humidity),
    pressure: Math.round(pressure),
    weatherMain,
    weatherDescription,
    weatherIcon,
    windSpeed: Math.round(windSpeed * 10) / 10,
    clouds,
    timestamp: new Date().toISOString(),
  };
}

/**
 * 기상청 데이터 파싱 (구버전 - 호환성 유지)
 */
function parseKMAWeatherData(items: any[], cityName: string): any {
  const data: Record<string, any> = {
    city: cityName,
    country: 'KR',
    temperature: 0,
    feelsLike: 0,
    humidity: 0,
    pressure: 1013,
    weatherMain: 'Clear',
    weatherDescription: '맑음',
    weatherIcon: '01d',
    windSpeed: 0,
    clouds: 0,
    timestamp: new Date().toISOString(),
  };

  items.forEach((item: any) => {
    const category = item.category;
    const value = parseFloat(item.obsrValue);

    switch (category) {
      case 'T1H': // 기온 (°C)
        data.temperature = Math.round(value);
        data.feelsLike = Math.round(value - 2); // 체감온도 근사값
        break;
      case 'RN1': // 1시간 강수량 (mm)
        if (value > 0) {
          data.weatherMain = 'Rain';
          data.weatherDescription = '비';
          data.weatherIcon = '10d';
        }
        break;
      case 'REH': // 습도 (%)
        data.humidity = Math.round(value);
        break;
      case 'WSD': // 풍속 (m/s)
        data.windSpeed = value;
        break;
      case 'SKY': // 하늘상태 (1:맑음, 3:구름많음, 4:흐림)
        if (value === 1) {
          data.weatherMain = 'Clear';
          data.weatherDescription = '맑음';
          data.weatherIcon = '01d';
          data.clouds = 10;
        } else if (value === 3) {
          data.weatherMain = 'Clouds';
          data.weatherDescription = '구름 많음';
          data.weatherIcon = '02d';
          data.clouds = 60;
        } else if (value === 4) {
          data.weatherMain = 'Clouds';
          data.weatherDescription = '흐림';
          data.weatherIcon = '04d';
          data.clouds = 90;
        }
        break;
      case 'PTY': // 강수형태 (0:없음, 1:비, 2:비/눈, 3:눈, 4:소나기)
        if (value === 1 || value === 4) {
          data.weatherMain = 'Rain';
          data.weatherDescription = value === 4 ? '소나기' : '비';
          data.weatherIcon = value === 4 ? '09d' : '10d';
        } else if (value === 2) {
          data.weatherMain = 'Rain';
          data.weatherDescription = '진눈깨비';
          data.weatherIcon = '13d';
        } else if (value === 3) {
          data.weatherMain = 'Snow';
          data.weatherDescription = '눈';
          data.weatherIcon = '13d';
        }
        break;
    }
  });

  return data;
}

/**
 * 실시간처럼 보이는 테스트 날씨 데이터 생성
 * (API 키 발급 전까지 임시 사용)
 */
function generateRealisticWeatherData(cityName: string): any {
  const now = new Date();
  const hour = now.getHours();
  const month = now.getMonth() + 1;
  
  // 시간대별 기온 변화
  let baseTemp = 15;
  if (hour >= 6 && hour < 9) baseTemp = 12; // 새벽
  else if (hour >= 9 && hour < 12) baseTemp = 18; // 오전
  else if (hour >= 12 && hour < 15) baseTemp = 22; // 낮
  else if (hour >= 15 && hour < 18) baseTemp = 20; // 오후
  else if (hour >= 18 && hour < 21) baseTemp = 16; // 저녁
  else baseTemp = 13; // 밤
  
  // 계절별 보정
  if (month >= 12 || month <= 2) baseTemp -= 8; // 겨울
  else if (month >= 3 && month <= 5) baseTemp += 2; // 봄
  else if (month >= 6 && month <= 8) baseTemp += 8; // 여름
  else baseTemp += 3; // 가을
  
  // 랜덤 변동 (-2 ~ +2)
  const tempVariation = Math.floor(Math.random() * 5) - 2;
  const temperature = baseTemp + tempVariation;
  
  // 날씨 상태 (확률 기반)
  const weatherRandom = Math.random();
  let weatherMain = 'Clear';
  let weatherDescription = '맑음';
  let weatherIcon = '01d';
  let clouds = 10;
  
  if (weatherRandom < 0.1) {
    // 10% 비
    weatherMain = 'Rain';
    weatherDescription = '비';
    weatherIcon = '10d';
    clouds = 80;
  } else if (weatherRandom < 0.25) {
    // 15% 흐림
    weatherMain = 'Clouds';
    weatherDescription = '흐림';
    weatherIcon = '04d';
    clouds = 90;
  } else if (weatherRandom < 0.5) {
    // 25% 구름 많음
    weatherMain = 'Clouds';
    weatherDescription = '구름 많음';
    weatherIcon = '02d';
    clouds = 60;
  }
  // 50% 맑음 (기본값)
  
  return {
    city: cityName,
    country: 'KR',
    temperature: Math.round(temperature),
    feelsLike: Math.round(temperature - 2),
    humidity: Math.floor(Math.random() * 30) + 50, // 50-80%
    pressure: Math.floor(Math.random() * 15) + 1008, // 1008-1023 hPa
    weatherMain,
    weatherDescription,
    weatherIcon,
    windSpeed: Math.random() * 4 + 1, // 1-5 m/s
    clouds,
    timestamp: now.toISOString(),
  };
}

/**
 * 공공데이터포털 초단기실황 데이터 파싱
 */
function parseKMADataWeatherData(data: any, gridCoord: { name: string; nx: number; ny: number }): any {
  const header = data.response?.header;
  const body = data.response?.body;
  
  if (header?.resultCode !== '00') {
    throw new Error(`API 오류: ${header?.resultMsg || '알 수 없는 오류'}`);
  }
  
  const items = body?.items?.item || [];
  
  if (items.length === 0) {
    throw new Error('날씨 데이터가 없습니다.');
  }
  
  // 초단기실황 응답 항목:
  // T1H: 기온, RN1: 1시간 강수량, REH: 습도, PTY: 강수형태, WSD: 풍속
  const weatherMap: Record<string, any> = {};
  items.forEach((item: any) => {
    weatherMap[item.category] = item.obsrValue;
  });
  
  const temperature = parseFloat(weatherMap['T1H']) || 0; // 기온
  const humidity = parseFloat(weatherMap['REH']) || 0; // 습도
  const rainfall = parseFloat(weatherMap['RN1']) || 0; // 1시간 강수량
  const precipType = parseInt(weatherMap['PTY']) || 0; // 강수형태 (0: 없음, 1: 비, 2: 비/눈, 3: 눈, 4: 소나기)
  const windSpeed = parseFloat(weatherMap['WSD']) || 0; // 풍속
  
  console.log(`📊 기상 데이터: ${gridCoord.name} - 기온 ${temperature}°C, 습도 ${humidity}%, 강수 ${rainfall}mm`);
  
  // 날씨 상태 결정
  let weatherMain = 'Clear';
  let weatherDescription = '맑음';
  let weatherIcon = '01d';
  let clouds = 10;
  
  // 강수형태 기반
  if (precipType > 0) {
    if (precipType === 1 || precipType === 4) {
      weatherMain = 'Rain';
      weatherDescription = rainfall >= 10 ? '비 (강수)' : '비';
      weatherIcon = rainfall >= 10 ? '10d' : '09d';
      clouds = 100;
    } else if (precipType === 2) {
      weatherMain = 'Snow';
      weatherDescription = '눈/비';
      weatherIcon = '13d';
      clouds = 100;
    } else if (precipType === 3) {
      weatherMain = 'Snow';
      weatherDescription = '눈';
      weatherIcon = '13d';
      clouds = 100;
    }
  }
  // 습도 기반
  else if (humidity > 80) {
    weatherMain = 'Clouds';
    weatherDescription = '흐림';
    weatherIcon = '04d';
    clouds = 90;
  } else if (humidity > 60) {
    weatherMain = 'Clouds';
    weatherDescription = '구름 많음';
    weatherIcon = '03d';
    clouds = 60;
  } else if (humidity > 40) {
    weatherMain = 'Clouds';
    weatherDescription = '구름 조금';
    weatherIcon = '02d';
    clouds = 30;
  }
  
  // 격자좌표 → 위도경도 변환
  const { lat, lng } = gridToLatLng(gridCoord.nx, gridCoord.ny);
  
  return {
    city: gridCoord.name,
    country: 'KR',
    lat,
    lng,
    temperature: Math.round(temperature),
    feelsLike: Math.round(temperature - 2),
    humidity: Math.round(humidity),
    pressure: 1013, // 초단기실황에는 기압 정보 없음
    weatherMain,
    weatherDescription,
    weatherIcon,
    windSpeed: Math.round(windSpeed * 10) / 10,
    clouds,
    timestamp: new Date().toISOString(),
  };
}

/**
 * 도시명 → 격자 좌표 매핑
 * 공공데이터포털 초단기실황 API는 격자(nx, ny) 기반
 */
function getGridCoordinates(city: string): { name: string; nx: number; ny: number } | null {
  const grids: Record<string, { name: string; nx: number; ny: number }> = {
    // 영어 도시명
    'Seoul': { name: '서울', nx: 60, ny: 127 },
    'Busan': { name: '부산', nx: 98, ny: 76 },
    'Incheon': { name: '인천', nx: 55, ny: 124 },
    'Daegu': { name: '대구', nx: 89, ny: 90 },
    'Gwangju': { name: '광주', nx: 58, ny: 74 },
    'Daejeon': { name: '대전', nx: 67, ny: 100 },
    'Ulsan': { name: '울산', nx: 102, ny: 84 },
    'Sejong': { name: '세종', nx: 66, ny: 103 },
    'Jeju': { name: '제주', nx: 52, ny: 38 },
    
    // 서울 (중구 기준)
    '서울': { name: '서울', nx: 60, ny: 127 },
    '종로구': { name: '서울 종로구', nx: 60, ny: 127 },
    '중구': { name: '서울 중구', nx: 60, ny: 127 },
    '용산구': { name: '서울 용산구', nx: 60, ny: 126 },
    '성동구': { name: '서울 성동구', nx: 61, ny: 127 },
    '광진구': { name: '서울 광진구', nx: 62, ny: 126 },
    '동대문구': { name: '서울 동대문구', nx: 61, ny: 127 },
    '중랑구': { name: '서울 중랑구', nx: 62, ny: 128 },
    '성북구': { name: '서울 성북구', nx: 61, ny: 127 },
    '강북구': { name: '서울 강북구', nx: 61, ny: 128 },
    '도봉구': { name: '서울 도봉구', nx: 61, ny: 129 },
    '노원구': { name: '서울 노원구', nx: 61, ny: 129 },
    '은평구': { name: '서울 은평구', nx: 59, ny: 127 },
    '서대문구': { name: '서울 서대문구', nx: 59, ny: 127 },
    '마포구': { name: '서울 마포구', nx: 59, ny: 127 },
    '양천구': { name: '서울 양천구', nx: 58, ny: 126 },
    '강서구': { name: '서울 강서구', nx: 58, ny: 126 },
    '구로구': { name: '서울 구로구', nx: 58, ny: 125 },
    '금천구': { name: '서울 금천구', nx: 59, ny: 124 },
    '영등포구': { name: '서울 영등포구', nx: 58, ny: 126 },
    '동작구': { name: '서울 동작구', nx: 59, ny: 125 },
    '관악구': { name: '서울 관악구', nx: 59, ny: 125 },
    '서초구': { name: '서울 서초구', nx: 61, ny: 125 },
    '강남구': { name: '서울 강남구', nx: 61, ny: 126 },
    '송파구': { name: '서울 송파구', nx: 62, ny: 126 },
    '강동구': { name: '서울 강동구', nx: 62, ny: 126 },
    
    // 부산
    '부산': { name: '부산', nx: 98, ny: 76 },
    '해운대': { name: '부산 해운대구', nx: 99, ny: 75 },
    '해운대구': { name: '부산 해운대구', nx: 99, ny: 75 },
    
    // 인천
    '인천': { name: '인천', nx: 55, ny: 124 },
    
    // 대구
    '대구': { name: '대구', nx: 89, ny: 90 },
    
    // 광주
    '광주': { name: '광주', nx: 58, ny: 74 },
    
    // 대전
    '대전': { name: '대전', nx: 67, ny: 100 },
    
    // 울산
    '울산': { name: '울산', nx: 102, ny: 84 },
    
    // 세종
    '세종': { name: '세종', nx: 66, ny: 103 },
    
    // 경기도 주요 도시
    '수원': { name: '수원', nx: 60, ny: 121 },
    '성남': { name: '성남', nx: 62, ny: 123 },
    '고양': { name: '고양', nx: 57, ny: 128 },
    '용인': { name: '용인', nx: 64, ny: 119 },
    '화성': { name: '화성', nx: 57, ny: 119 },
    '부천': { name: '부천', nx: 56, ny: 125 },
    
    // 화성시 읍/면/동
    '영천동': { name: '화성 영천동', nx: 57, ny: 119 },
    '화성 영천동': { name: '화성 영천동', nx: 57, ny: 119 },
    '봉담읍': { name: '화성 봉담읍', nx: 57, ny: 120 },
    '화성 봉담읍': { name: '화성 봉담읍', nx: 57, ny: 120 },
    '동탄': { name: '화성 동탄', nx: 61, ny: 120 },
    '화성 동탄': { name: '화성 동탄', nx: 61, ny: 120 },
    
    // 성남시 구/동
    '분당구': { name: '성남 분당구', nx: 62, ny: 123 },
    '성남 분당구': { name: '성남 분당구', nx: 62, ny: 123 },
    '분당': { name: '성남 분당구', nx: 62, ny: 123 },
    '판교': { name: '성남 판교', nx: 62, ny: 124 },
    '성남 판교': { name: '성남 판교', nx: 62, ny: 124 },
    
    // 용인시
    '수지구': { name: '용인 수지구', nx: 62, ny: 121 },
    '용인 수지구': { name: '용인 수지구', nx: 62, ny: 121 },
    '기흥구': { name: '용인 기흥구', nx: 61, ny: 120 },
    '용인 기흥구': { name: '용인 기흥구', nx: 61, ny: 120 },
    '처인구': { name: '용인 처인구', nx: 64, ny: 119 },
    '용인 처인구': { name: '용인 처인구', nx: 64, ny: 119 },
    
    // 강원도
    '춘천': { name: '춘천', nx: 73, ny: 134 },
    '강릉': { name: '강릉', nx: 92, ny: 131 },
    '원주': { name: '원주', nx: 76, ny: 122 },
    
    // 충청도
    '청주': { name: '청주', nx: 69, ny: 107 },
    '천안': { name: '천안', nx: 63, ny: 110 },
    
    // 전라도
    '전주': { name: '전주', nx: 63, ny: 89 },
    '목포': { name: '목포', nx: 50, ny: 67 },
    '여수': { name: '여수', nx: 73, ny: 66 },
    
    // 경상도
    '포항': { name: '포항', nx: 102, ny: 94 },
    '창원': { name: '창원', nx: 90, ny: 77 },
    '진주': { name: '진주', nx: 90, ny: 75 },
    
    // 제주도
    '제주': { name: '제주', nx: 52, ny: 38 },
    '서귀포': { name: '서귀포', nx: 52, ny: 33 },
  };
  
  return grids[city] || null;
}

/**
 * 격자좌표(nx, ny)를 위도경도로 변환
 * 기상청 격자 → 위경도 변환 공식 사용
 */
function gridToLatLng(nx: number, ny: number): { lat: number; lng: number } {
  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좌표
  const YO = 136; // 기준점 Y좌표

  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;

  const sn = Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5);
  const sn_log = Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(sn);
  const sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
  const sf_pow = Math.pow(sf, sn_log);
  const sf_result = (Math.cos(slat1) * sf_pow) / sn_log;
  const ro = Math.tan(Math.PI * 0.25 + olat * 0.5);
  const ro_pow = Math.pow(ro, sn_log);
  const ro_result = (re * sf_result) / ro_pow;

  const xn = nx - XO;
  const yn = ro_result - (ny - YO);
  const ra = Math.sqrt(xn * xn + yn * yn);
  let alat: number;

  if (sn_log > 0) {
    alat = 2.0 * Math.atan(Math.pow((re * sf_result) / ra, 1.0 / sn_log)) - Math.PI * 0.5;
  } else {
    alat = -2.0 * Math.atan(Math.pow((re * sf_result) / ra, 1.0 / sn_log)) + Math.PI * 0.5;
  }

  let theta: number;
  if (Math.abs(xn) <= 0.0) {
    theta = 0.0;
  } else {
    if (Math.abs(yn) <= 0.0) {
      theta = 0.0;
    } else {
      theta = Math.atan2(xn, yn);
    }
  }
  const alon = theta / sn_log + olon;

  return {
    lat: parseFloat((alat / DEGRAD).toFixed(6)),
    lng: parseFloat((alon / DEGRAD).toFixed(6)),
  };
}

/**
 * 공공데이터포털 초단기실황 응답 파싱
 * @param apiResponse - 공공데이터포털 API 응답 데이터
 * @param gridInfo - 격자 좌표 정보
 * @returns 표준화된 날씨 데이터
 */
function parseDataPortalWeatherData(apiResponse: any, gridInfo: { name: string; nx: number; ny: number }) {
  try {
    const response = apiResponse.response;
    const header = response.header;
    const body = response.body;

    // API 응답 코드 확인
    if (header.resultCode !== '00') {
      console.error('❌ 공공데이터포털 API 오류:', header.resultMsg);
      throw new Error(`API Error: ${header.resultMsg}`);
    }

    const items = body.items.item;
    if (!items || items.length === 0) {
      throw new Error('날씨 데이터가 없습니다.');
    }

    // 카테고리별 데이터 추출
    const dataMap: Record<string, string> = {};
    items.forEach((item: any) => {
      dataMap[item.category] = item.obsrValue;
    });

    // T1H: 기온(°C)
    // RN1: 1시간 강수량(mm)
    // REH: 습도(%)
    // WSD: 풍속(m/s)
    // VEC: 풍향(deg)
    // PTY: 강수형태 (0:없음, 1:비, 2:비/눈, 3:눈, 5:빗방울, 6:빗방울눈날림, 7:눈날림)

    const temperature = parseFloat(dataMap['T1H'] || '0');
    const humidity = parseFloat(dataMap['REH'] || '0');
    const windSpeed = parseFloat(dataMap['WSD'] || '0');
    const precipitation = parseFloat(dataMap['RN1'] || '0');
    const ptyCode = dataMap['PTY'] || '0';

    // 강수형태 → 날씨 상태 매핑
    let weatherStatus = 'clear';
    let weatherDescription = '맑음';
    
    if (ptyCode === '1' || ptyCode === '5') {
      weatherStatus = 'rain';
      weatherDescription = '비';
    } else if (ptyCode === '2' || ptyCode === '6') {
      weatherStatus = 'rain';
      weatherDescription = '비/눈';
    } else if (ptyCode === '3' || ptyCode === '7') {
      weatherStatus = 'snow';
      weatherDescription = '눈';
    } else if (temperature > 25) {
      weatherDescription = '맑음';
    } else if (temperature < 5) {
      weatherDescription = '추움';
    }

    // 격자좌표 → 위도경도 변환
    const { lat, lng } = gridToLatLng(gridInfo.nx, gridInfo.ny);

    return {
      city: gridInfo.name,
      lat,
      lng,
      temperature: Math.round(temperature * 10) / 10,
      humidity: Math.round(humidity),
      windSpeed: Math.round(windSpeed * 10) / 10,
      precipitation: Math.round(precipitation * 10) / 10,
      weather: weatherStatus,
      description: weatherDescription,
      timestamp: new Date().toISOString(),
    };
  } catch (error) {
    console.error('❌ 공공데이터포털 응답 파싱 실패:', error);
    throw error;
  }
}

/**
 * 통화 코드 → 한국은행 통계코드 매핑
 * @param currencyCode - ISO 통화 코드 (USD, EUR, JPY 등)
 * @returns 한국은행 통계코드
 */
function getBOKStatCode(currencyCode: string): string | null {
  const statCodes: Record<string, string> = {
    // 주요 통화 (매일 고시)
    'USD': '0000001', // 미국 달러
    'JPY': '0000002', // 일본 엔 (100엔)
    'EUR': '0000003', // 유럽연합 유로
    'CNY': '0000053', // 중국 위안
    'GBP': '0000002', // 영국 파운드
    'AUD': '0000007', // 호주 달러
    'CAD': '0000009', // 캐나다 달러
    'CHF': '0000017', // 스위스 프랑
    'HKD': '0000019', // 홍콩 달러
    'SEK': '0000020', // 스웨덴 크로나
    'NOK': '0000021', // 노르웨이 크로네
    'DKK': '0000022', // 덴마크 크로네
    'SGD': '0000024', // 싱가포르 달러
    'NZD': '0000026', // 뉴질랜드 달러
    'THB': '0000028', // 태국 바트
    'MYR': '0000029', // 말레이시아 링깃
    'IDR': '0000030', // 인도네시아 루피아 (100루피아)
    'PHP': '0000031', // 필리핀 페소
    'INR': '0000032', // 인도 루피
    'BRL': '0000034', // 브라질 레알
  };

  return statCodes[currencyCode] || null;
}

/**
 * 한국은행 API 응답 파싱
 * @param apiResponse - 한국은행 API 응답 데이터
 * @param base - 기준 통화
 * @param target - 대상 통화
 * @param isReverse - 역수 계산 필요 여부 (KRW → USD)
 * @returns 표준화된 환율 데이터
 */
function parseBOKExchangeData(
  apiResponse: any,
  base: string,
  target: string,
  isReverse: boolean
) {
  try {
    // API 응답 구조: { StatisticSearch: { list_total_count, row: [...] } }
    const result = apiResponse.StatisticSearch;
    
    if (!result || result.list_total_count === '0') {
      throw new Error('환율 데이터가 없습니다. (주말/공휴일일 수 있습니다)');
    }

    const row = result.row[0];
    const rate = parseFloat(row.DATA_VALUE);

    // KRW → USD (역수 계산)
    const finalRate = isReverse ? (1 / rate) : rate;

    return {
      base,
      target,
      rate: Math.round(finalRate * 100) / 100, // 소수점 2자리
      timestamp: new Date().toISOString(),
      source: '한국은행 (BOK)',
    };
  } catch (error) {
    console.error('❌ 한국은행 API 응답 파싱 실패:', error);
    throw error;
  }
}

/**
 * 테스트 환율 데이터 생성
 * @param base - 기준 통화
 * @param target - 대상 통화
 * @returns 테스트 환율 데이터
 */
function generateTestExchangeRate(base: string, target: string) {
  // 실제 환율 기준 (2024년 평균)
  const baseRates: Record<string, number> = {
    'USD': 1300,   // 1 USD = 1300 KRW
    'EUR': 1400,   // 1 EUR = 1400 KRW
    'JPY': 9.0,    // 1 JPY = 9 KRW (100엔 기준)
    'CNY': 180,    // 1 CNY = 180 KRW
    'GBP': 1650,   // 1 GBP = 1650 KRW
  };

  let rate: number;

  if (base === 'KRW' && baseRates[target]) {
    // KRW → USD: 1/1300 = 0.00077
    rate = 1 / baseRates[target];
  } else if (target === 'KRW' && baseRates[base]) {
    // USD → KRW: 1300
    rate = baseRates[base];
  } else if (base === 'KRW' && target === 'KRW') {
    rate = 1;
  } else {
    // 지원하지 않는 통화 쌍
    rate = 1.0;
  }

  // 약간의 랜덤 변동 (±1%)
  const variation = 0.98 + Math.random() * 0.04;
  rate = rate * variation;

  return {
    base,
    target,
    rate: Math.round(rate * 100) / 100,
    timestamp: new Date().toISOString(),
    source: 'TEST_DATA',
  };
}


/**
 * 실제 근사값 환율 데이터 생성
 */
function generateRealisticExchangeRate(base: string, target: string) {
  const baseRates: Record<string, number> = {
    'USD': 1380, 'EUR': 1500, 'JPY': 9.2, 'CNY': 195, 'GBP': 1790,
  };
  let rate = 1;
  if (base === 'KRW' && baseRates[target]) {
    rate = 1 / baseRates[target];
  } else if (target === 'KRW' && baseRates[base]) {
    rate = baseRates[base];
  } else if (baseRates[base] && baseRates[target]) {
    rate = baseRates[target] / baseRates[base];
  }
  return { base, target, rate, timestamp: new Date().toISOString(), source: 'ExchangeRate-API (Cache)' };
}
