import type { Module } from 'vuex'
import type { RootState } from '~/store/index'

import axios from 'axios'
import { merge } from 'lodash-es'
import useLogger from '~/composables/useLogger'
import { useQueue } from '~/composables/audio/queue'
import { isTauri } from '~/composables/tauri'

export interface State {
  frontSettings: FrontendSettings
  instanceUrl?: string
  knownInstances: string[]
  nodeinfo: NodeInfo | null
  settings: Settings
}

type TotalCount = {
  total: number
}

export interface NodeInfo {
  version: string;
  software: {
    name: string;
    version: string;
  }
  protocols: any[];
  services?: {
    inbound?: string[];
    outbound?: string[];
  }
  openRegistrations: boolean;
  usage: {
    users: {
      total: number;
      activeHalfyear: number;
      activeMonth: number;
    }
  }
  metadata: {
    actorId: string
    'private': boolean
    shortDescription: string
    longDescription: string
    rules: string
    contactEmail: string
    terms: string
    nodeName: string
    banner: string
    defaultUploadQuota: number
    content: {
      federationEnabled: boolean
      anonymousCanListen: boolean
      local: {
        tracks?: TotalCount
        artists?: TotalCount
        albums?: TotalCount
        hoursOfContent?: number }
    }
    topMusicCategories: []
    topPodcastCategories: []
    federation: {
      followedInstances: number
      followingInstances: number
    }
    supportedUploadExtensions: string[]
    allowList: {
      enabled: boolean
      domains: string[]
    }
    reportTypes: {
      'type': string
      label: string
      anonymous: boolean
    }[]
    funkwhaleSupportMessageEnabled: boolean
    instanceSupportMessage: string
    endpoints: {
      knownNodes?: string
      channels?: string
      libraries?: string
    }
    usage: {
      favorites: { tracks: TotalCount }
      listenings: TotalCount
      downloads: TotalCount
    }
    features:[]
  }
}

interface FrontendSettings {
  defaultServerUrl: string
  additionalStylesheets: string[]
}

interface InstanceSettings {
  name: { value: string }
  short_description: { value: string }
  long_description: { value: string }
  funkwhale_support_message_enabled: { value: boolean }
  support_message: { value: string }
}

interface UsersSettings {
  registration_enabled: { value: boolean }
  upload_quota: { value: number }
}

interface ModerationSettings {
  signup_approval_enabled: { value: boolean }
  signup_form_customization: { value: null }
}
interface QualityFiltersSettings {
  [key: string]: any;

  bitrate_filter: { value: number }
  has_mbid: { value: boolean }
}

interface SubsonicSettings {
  enabled: { value: boolean }
}

interface UISettings {
  custom_css: { value: string }
}

interface Settings {
  instance: InstanceSettings
  users: UsersSettings
  moderation: ModerationSettings
  quality_filters: QualityFiltersSettings
  subsonic: SubsonicSettings
  ui: UISettings
}

const logger = useLogger()

// Use some arbitrary url that will trigger the instance chooser, this needs to be a valid url
export const TAURI_DEFAULT_INSTANCE_URL = 'http://localhost/force-instance-chooser/'

// We have several way to guess the API server url. By order of precedence:
// 0. use the url provided in settings.json, if any. That's a lazy operation done by already initialized store.
// 1. force instance chooser, if in tauri app
// 2. use the url specified when building via VUE_APP_INSTANCE_URL
// 3. use the current url
export const findDefaultInstanceUrl = () => {
  if (isTauri()) {
    return TAURI_DEFAULT_INSTANCE_URL
  }

  try {
    return new URL(import.meta.env.VUE_APP_INSTANCE_URL as string).href
  } catch (e) {
    logger.warn('Invalid VUE_APP_INSTANCE_URL, falling back to current url', e)
  }

  return `${location.origin}/`
}

const DEFAULT_INSTANCE_URL = findDefaultInstanceUrl()

const store: Module<State, RootState> = {
  namespaced: true,
  state: {
    frontSettings: {
      defaultServerUrl: DEFAULT_INSTANCE_URL,
      additionalStylesheets: []
    },
    instanceUrl: DEFAULT_INSTANCE_URL,
    knownInstances: [],
    nodeinfo: null,
    settings: {
      instance: {
        name: {
          value: ''
        },
        short_description: {
          value: ''
        },
        long_description: {
          value: ''
        },
        funkwhale_support_message_enabled: {
          value: true
        },
        support_message: {
          value: ''
        }
      },
      users: {
        registration_enabled: {
          value: true
        },
        upload_quota: {
          value: 0
        }
      },
      moderation: {
        signup_approval_enabled: {
          value: false
        },
        signup_form_customization: { value: null }
      },
      quality_filters: {
        bitrate_filter: { value: 0 },
        has_mbid: { value: false }
      },
      subsonic: {
        enabled: {
          value: true
        }
      },
      ui: {
        custom_css: {
          value: ''
        }
      }
    }
  },
  mutations: {
    settings: (state, value) => {
      merge(state.settings, value)
    },
    nodeinfo: (state, value) => {
      state.nodeinfo = value
    },
    instanceUrl: (state, value) => {
      try {
        const { href } = new URL(value)
        state.instanceUrl = href
        axios.defaults.baseURL = `${href}api/v2/`

        // append the URL to the list (and remove existing one if needed)
        const index = state.knownInstances.indexOf(href)
        if (index > -1) state.knownInstances.splice(index, 1)
        state.knownInstances.unshift(href)
      } catch (e) {
        logger.error('Invalid instance URL', e)
        axios.defaults.baseURL = undefined
      }
    }
  },
  getters: {
    absoluteUrl: (_state, getters) => (relativeUrl: string) => {
      if (relativeUrl.startsWith('http')) return relativeUrl
      return relativeUrl.startsWith('/')
        ? `${getters.url.href}${relativeUrl.slice(1)}`
        : `${getters.url.href}${relativeUrl}`
    },
    url: (state) => new URL(state.instanceUrl ?? DEFAULT_INSTANCE_URL),
    domain: (_state, getters) => getters.url.hostname,
    defaultInstance: () => DEFAULT_INSTANCE_URL,
    qualityFilters: (state) => {
      const qualityFilters = state.settings.quality_filters
      const filteredQualityFilters: Record<string, any> = {}

      for (const key in qualityFilters) {
        if (Object.prototype.hasOwnProperty.call(qualityFilters, key)) {
          if (qualityFilters[key].value === false) {
            continue
          } else {
            filteredQualityFilters[key] = qualityFilters[key].value
          }
        }
      }
      return filteredQualityFilters
    }
  },
  actions: {
    setUrl ({ commit }, url) {
      commit('instanceUrl', url)
      const modules = [
        'auth',
        'favorites',
        'moderation',
        'player',
        'playlists',
        'queue',
        'radios'
      ]
      modules.forEach(m => {
        commit(`${m}/reset`, null, { root: true })
      })

      const { clear } = useQueue()
      return clear()
    },
    async fetchSettings ({ commit }) {
      const response = await axios.get('instance/settings/')
        .catch(err => logger.error('Error while fetching settings', err.response.data))

      if (!response?.data || !Array.isArray(response?.data)) return

      logger.info('Successfully fetched instance settings')

      type SettingsSection = { section: string, name: string }
      const sections = response.data.reduce((map: Record<string, Record<string, SettingsSection>>, entry: SettingsSection) => {
        map[entry.section] ??= {}
        map[entry.section][entry.name] = entry
        return map
      }, {})

      commit('settings', sections)
    },
    async fetchFrontSettings ({ state }) {
      const response = await axios.get(`${import.meta.env.BASE_URL}settings.json`)
        .catch(() => logger.error('Error when fetching front-end configuration (or no customization available)'))

      if (!response) return

      for (const [key, value] of Object.entries(response.data as FrontendSettings)) {
        if (key === 'defaultServerUrl' && !value) {
          state.frontSettings.defaultServerUrl = DEFAULT_INSTANCE_URL
          continue
        }

        state.frontSettings[key as keyof FrontendSettings] = value
      }
    }
  }
}

export default store
