import STATIC_LOCATIONS from './static-ens-locations.json'

// https://stackoverflow.com/a/35970186
function invertColor(hex) {
  if (hex.indexOf('#') === 0) {
    hex = hex.slice(1)
  }
  // convert 3-digit hex to 6-digits.
  if (hex.length === 3) {
    hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]
  }
  if (hex.length !== 6) {
    throw new Error('Invalid HEX color.')
  }
  var r = parseInt(hex.slice(0, 2), 16),
    g = parseInt(hex.slice(2, 4), 16),
    b = parseInt(hex.slice(4, 6), 16)
  // https://stackoverflow.com/a/3943023/112731
  return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? '#000000' : '#FFFFFF'
}

const clouds = {
  KLUB_RESEAU: 'klub-reseau',
  ELEVES_ENS: 'eleves-ens',
  FRAMA_AGENDA: 'frama-agenda'
}

const calendars = {
  '5WrcagPPARQ3BD87': {
    cloud: clouds.KLUB_RESEAU,
    name: 'Club réseau',
    color: null,
    default_location: "Cave d'hackENS"
  },
  TFEAKjAgNFQZpNjo: {
    cloud: clouds.KLUB_RESEAU,
    name: 'hackENS',
    color: null,
    default_location: "Cave d'hackENS"
  },
  LLWm8qK9iC5YGrrR: {
    cloud: clouds.ELEVES_ENS,
    name: 'Délégation Générale',
    short_name: 'DG',
    color: null
  },
  '2KGkWzBJGorxzyTW': {
    cloud: clouds.ELEVES_ENS,
    name: "La Nuit de l'ENS",
    short_name: 'La Nuit',
    color: null
  },
  w442JdS5AaQ6czrP: {
    cloud: clouds.ELEVES_ENS,
    name: "Écriv'ENS",
    color: null
  },
  fRtjDkjrZyn6fxd8: {
    cloud: clouds.ELEVES_ENS,
    name: 'K-Fêt',
    color: '#c63b52',
    default_location: 'K-Fêt'
  },
  gsZtZK8c9EmREofn: {
    cloud: clouds.ELEVES_ENS,
    name: 'Ernestophone',
    color: null
  },
  dTHrXnYgsEoSTjWB: {
    cloud: clouds.ELEVES_ENS,
    name: 'Évènements (COF)',
    short_name: 'COF',
    color: null
  },
  bCgRFByHLiCCNc55: {
    cloud: clouds.ELEVES_ENS,
    name: 'Assemblées Générales (COF)',
    short_name: 'AG COF',
    color: null
  },
  r4yJZDHjwNtH8wkR: {
    cloud: clouds.ELEVES_ENS,
    name: 'BdA',
    color: null
  },
  T5WoHbs4FT5A945Z: {
    cloud: clouds.FRAMA_AGENDA,
    name: 'CinéClub',
    color: null
  },
  '6SHG6cg9d7S3qqwD': {
    cloud: clouds.ELEVES_ENS,
    name: 'Club Inutile ☔',
    color: null,
    initial: false
  },
  Ekjb4kDqMMqwJXZF: {
    cloud: clouds.ELEVES_ENS,
    name: 'Rentrée des départements',
    short_name: 'Dpt',
    color: null
  },
  '8SKP62tQJP65K8EW': {
    cloud: clouds.ELEVES_ENS,
    name: 'Conférences de recherche',
    short_name: 'Conf',
    color: null
  },
  PnRXqeq4SsSC33FM: {
    cloud: clouds.ELEVES_ENS,
    name: 'Visites de bibliothèques',
    short_name: 'Bibli',
    initial: false,
    color: null
  },
  NWPtiEiz62LTtjo2: {
    cloud: clouds.ELEVES_ENS,
    name: 'Amphis de rentrée',
    short_name: 'Prés. de rentrée',
    color: null
  },
  JiRt58aJXay9kfyk: {
    cloud: clouds.ELEVES_ENS,
    name: 'Réunions de rentrée des Masters',
    short_name: 'Masters',
    color: null
  },
  '5Rb4bRjCDcsFjDdQ': {
    cloud: clouds.ELEVES_ENS,
    name: 'Activités pour les étudiants internationaux',
    short_name: 'Internationaux',
    color: null
  },
  '62wKfQRrLNz2WXjt': {
    cloud: clouds.ELEVES_ENS,
    name: 'Divers',
    color: null
  }
}

const calendarsByName = Object.fromEntries(
  Object.entries(calendars).map(([id, { name }]) => [name, id])
)

export const initialCalendars = Array.from(
  Object.entries(calendars).map(([_, { name, initial }]) => [name, initial ?? true])
)
  .filter(cal => cal[1])
  .map(cal => cal[0])

export const calendarTree = {
  COF: {
    BdA: {},
    'Évènements (COF)': {},
    'Assemblées Générales (COF)': {}
  },
  'Clubs COF': {
    'Club réseau': {},
    hackENS: {},
    "Écriv'ENS": {},
    CinéClub: {},
    Ernestophone: {},
    'Club Inutile ☔': {}
  },
  "La Nuit de l'ENS": {},
  'Délégation Générale': {},
  'K-Fêt': {},
  'Rentrée académique': {
    'Amphis de rentrée': {},
    'Rentrée des départements': {},
    'Conférences de recherche': {},
    'Visites de bibliothèques': {},
    'Réunions de rentrée des Masters': {},
    'Activités pour les étudiants internationaux': {}
  },
  Divers: {}
}

export function getSubCalendars(name, tree = calendarTree) {
  let ret
  for (const [cal, subTree] of Object.entries(tree)) {
    if (cal === name) {
      ret = subTree
    } else {
      ret = ret || getSubCalendars(name, subTree)
    }
  }
  return ret
}

const calendarIds = Object.keys(calendars)

function mkCalendarUrl(id, { cloud }, extra = {}) {
  return (
    `/cal/${cloud}/${id}/?` +
    new URLSearchParams({
      ...extra,
      export: true,
      expand: true,
      accept: 'jcal'
    })
  )
}

function mkExportUrl(id, { cloud }) {
  return `/cal/${cloud}/${id}/?export`
}

function fetchCalendar(id, cal, extra = {}) {
  return fetch(mkCalendarUrl(id, cal, extra), { credentials: 'omit' })
    .then(resp => resp.json())
    .catch(err => console.error(err))
}

class Calendar {
  constructor(id, calendar) {
    const metadata = calendars[id]
    this.name = metadata.name
    this.short_name = metadata.short_name
    this.color = metadata.color || calendar[1][4][3]
    this.default_location = metadata.default_location
    this.events = calendar[2]
      .filter(item => item[0] === 'vevent')
      .map(item => this._parse_vevent(item[1]))
  }

  _parse_vevent(vevent) {
    const event = {}
    vevent.forEach(elt => {
      event[elt[0]] = elt[3]
    })
    return event
  }
}

function findLocationId(location) {
  const adhocMap = {
    'Amphi Jourdan': 'Amphithéâtre Jourdan',
    'R2-21 (Jourdan)': 'R2-21',
    'Salle Jean Ibanes (Jourdan, R1-07)': 'Salle Jean Ibanes (R1-07)',
    'Salle Madeleine Rebérioux (Jourdan, R2-02)': 'Salle Madeleine Rebérioux (R2-02)',
    'Salle Marcel Roncayolo (Jourdan, R2-05)': 'Salle Marcel Roncayolo (R2-05)'
  };

  const correctedLocation = adhocMap[location] || location;

  const result = Object.entries(STATIC_LOCATIONS).find(([building, rooms]) =>
    rooms.includes(correctedLocation)
  )

  if (result === undefined) return undefined
  const [building, _] = result
  return `${building}-${correctedLocation}`
}

function fcEventFromjCalEvent(cal) {
  return function (evt) {
    const start = new Date(evt.dtstart)
    const end = new Date(evt.dtend)
    const fcEvent = {
      title: `${cal.short_name ?? cal.name} : ${evt.summary}`,
      start: evt.dtstart,
      end: evt.dtend,
      color: cal.color,
      textColor: invertColor(cal.color),
      duration: end - start // in ms
    }

    fcEvent.calendar = cal.name
    fcEvent.short_name = evt.summary
    fcEvent.description = evt.description
    fcEvent.location = evt.location || cal.default_location

    if (fcEvent.location) {
      fcEvent.resourceId = findLocationId(fcEvent.location)
    }

    if (evt.status) {
      fcEvent.status = evt.status
      fcEvent.classNames = [`st-${evt.status.toLowerCase()}`]
    }

    if (evt.rrule) {
      const { freq, byday, interval } = evt.rrule
      fcEvent.rrule = {
        freq,
        byweekday: byday,
        dtstart: evt.dtstart
      }

      if (interval) {
        fcEvent.rrule.interval = interval
      }
    }

    return fcEvent
  }
}

function mkEventsFromCalendar(id, cal) {
  return fetchCalendar(id, cal).then(calendar => {
    if (calendar[0] !== 'vcalendar') return
    const cal = new Calendar(id, calendar)
    return cal.events.map(fcEventFromjCalEvent(cal))
  })
}

export function mkSource(name) {
  const calendarId = calendarsByName[name]
  if (!calendarId) return null
  const calendar = calendars[calendarId]

  return {
    id: name,
    ...(calendar?.meta || {}),
    success: calendarData => {
      if (calendarData[0] !== 'vcalendar') return
      const cal = new Calendar(calendarId, calendarData)
      return cal.events.map(fcEventFromjCalEvent(cal))
    },
    failure: error => {
      console.error(`Fatal error during event source fetching of '${name}': ${error}`)
    },
    events: (info, successCallback, failureCallback) => {
      const { start, end } = info
      fetchCalendar(calendarId, calendar, {
        start: start.valueOf() / 1000,
        end: end.valueOf() / 1000
      }).then(successCallback, failureCallback)
    },
    export_url: mkExportUrl(calendarId, calendar)
  }
}
