import Club from '../models/Club.js';
import { checkAccess } from '../utils/userUtils.js';

const OPEN_HOLIDAYS_BASE_URL = 'https://openholidaysapi.org';
const CACHE_TTL_MS = 1000 * 60 * 60 * 24 * 14;
const REQUEST_TIMEOUT_MS = 8000;
const cache = new Map();

class CalendarHolidayService {
  async getClubCalendarDays(userToken, clubId, year) {
    await checkAccess(userToken, clubId);
    const club = await Club.findByPk(clubId);
    if (!club) {
      throw new Error('clubnotfound');
    }

    const normalizedYear = this.normalizeYear(year);
    const countryCode = this.normalizeCountryCode(club.countryCode);
    const stateCode = this.normalizeStateCode(club.stateCode);

    if (!countryCode || !stateCode) {
      return {
        countryCode,
        stateCode,
        year: normalizedYear,
        holidays: [],
        schoolHolidays: []
      };
    }

    const cacheKey = `${countryCode}:${stateCode}:${normalizedYear}`;
    const cached = cache.get(cacheKey);
    if (cached && cached.expiresAt > Date.now()) {
      return cached.data;
    }

    const validFrom = `${normalizedYear}-01-01`;
    const validTo = `${normalizedYear}-12-31`;
    const [holidays, schoolHolidays] = await Promise.all([
      this.fetchOpenHolidays('PublicHolidays', { countryCode, stateCode, validFrom, validTo }),
      this.fetchOpenHolidays('SchoolHolidays', { countryCode, stateCode, validFrom, validTo })
    ]);

    const data = {
      countryCode,
      stateCode,
      year: normalizedYear,
      holidays: holidays
        .filter(item => this.isRelevantForSubdivision(item, stateCode))
        .map(item => this.normalizeOpenHoliday(item, 'holiday'))
        .filter(Boolean),
      schoolHolidays: schoolHolidays.map(item => this.normalizeOpenHoliday(item, 'schoolHoliday')).filter(Boolean)
    };
    cache.set(cacheKey, { data, expiresAt: Date.now() + CACHE_TTL_MS });
    return data;
  }

  async fetchOpenHolidays(endpoint, { countryCode, stateCode, validFrom, validTo }) {
    const params = new URLSearchParams({
      countryIsoCode: countryCode,
      languageIsoCode: 'DE',
      validFrom,
      validTo
    });
    if (endpoint === 'SchoolHolidays') {
      params.set('subdivisionCode', stateCode);
    }
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);

    try {
      const response = await fetch(`${OPEN_HOLIDAYS_BASE_URL}/${endpoint}?${params.toString()}`, {
        headers: { accept: 'text/json' },
        signal: controller.signal
      });
      if (!response.ok) {
        throw new Error(`OpenHolidays ${endpoint} HTTP ${response.status}`);
      }
      const data = await response.json();
      return Array.isArray(data) ? data : [];
    } finally {
      clearTimeout(timeout);
    }
  }

  normalizeOpenHoliday(item, type) {
    const startDate = item?.startDate || item?.date || item?.validFrom;
    const endDate = item?.endDate || item?.date || item?.validTo || startDate;
    if (!startDate || !endDate) return null;

    return {
      id: item.id || `${type}-${startDate}-${this.getName(item)}`,
      type,
      startDate: String(startDate).slice(0, 10),
      endDate: String(endDate).slice(0, 10),
      name: this.getName(item)
    };
  }

  getName(item) {
    const names = Array.isArray(item?.name) ? item.name : [];
    const germanName = names.find(name => String(name.language || name.languageIsoCode || '').toUpperCase() === 'DE');
    const firstName = germanName || names[0];
    if (firstName?.text) return firstName.text;
    if (typeof item?.name === 'string') return item.name;
    if (typeof item?.localName === 'string') return item.localName;
    return 'Kalendereintrag';
  }

  isRelevantForSubdivision(item, stateCode) {
    const subdivisions = Array.isArray(item?.subdivisions) ? item.subdivisions : [];
    if (subdivisions.length === 0) return true;
    return subdivisions.some(subdivision => String(subdivision?.code || '').toUpperCase() === stateCode);
  }

  normalizeYear(year) {
    const parsed = Number.parseInt(year, 10);
    if (Number.isInteger(parsed) && parsed >= 2020 && parsed <= 2100) {
      return parsed;
    }
    return new Date().getFullYear();
  }

  normalizeCountryCode(countryCode) {
    const normalized = String(countryCode || 'DE').trim().toUpperCase();
    return /^[A-Z]{2}$/.test(normalized) ? normalized : 'DE';
  }

  normalizeStateCode(stateCode) {
    return String(stateCode || '').trim().toUpperCase();
  }
}

export default new CalendarHolidayService();
