import { CATEGORIES,
	 FETCH_POSTS_ORDER_FIELDS,
	 PLATFORMS,
	 USER_ROLES,
	 ViewFlagBehavior } from '@/consts'

import type { ReactNode } from 'react'

export type Category = typeof CATEGORIES[number]

export type Deerjikist = { platform: Platform; code: string }

export type FetchPostsOrder = `${ FetchPostsOrderField }:${ 'asc' | 'desc' }`

export type FetchPostsOrderField = typeof FETCH_POSTS_ORDER_FIELDS[number]

export type FetchPostsParams = {
    url:                  string
    title:                string
    tags:                 string
    match:                'all' | 'any'
    originalCreatedFrom:  string
    originalCreatedTo:    string
    createdFrom:          string
    createdTo:            string
    updatedFrom:          string
    updatedTo:            string
    page:                 number
    limit:                number
    order:                FetchPostsOrder }

export type FetchTagsOrder = `${ FetchTagsOrderField }:${ 'asc' | 'desc' }`

export type FetchTagsOrderField =
    | 'name'
    | 'category'
    | 'post_count'
    | 'created_at'
    | 'updated_at'

export type FetchTagsParams = {
    post:           number | null
    name:           string
    category:       Category | null
    postCountGTE:   number
    postCountLTE:   number | null
    createdFrom:    string
    createdTo:      string
    updatedFrom:    string
    updatedTo:      string
    deprecated:     boolean | null
    page:           number
    limit:          number
    order:          FetchTagsOrder }

export type FetchNicoTagsParams = {
    name:       string
    linkedTag:  string
    linkStatus: 'all' | 'linked' | 'unlinked'
    page:       number
    limit:      number
    order:      FetchNicoTagsOrder }

export type FetchNicoTagsOrder = `${ FetchNicoTagsOrderField }:${ 'asc' | 'desc' }`

export type FetchNicoTagsOrderField = 'name' | 'created_at' | 'updated_at'

export type MaterialFilter = 'present' | 'missing' | 'any'

export type MaterialIndexDirection = 'asc' | 'desc'

export type MaterialIndexMediaKind =
    | 'all'
    | 'image'
    | 'video'
    | 'audio'
    | 'file_other'
    | 'url_only'

export type MaterialIndexSort =
    | 'created_at'
    | 'updated_at'
    | 'tag_name'
    | 'media_kind'
    | 'file_byte_size'
    | 'version_no'
    | 'id'

export type MaterialIndexTagState = 'all' | 'tagged' | 'untagged'

export type MaterialIndexGroupBy = 'none' | 'parent_tag'

export type MaterialIndexView = 'card' | 'list'

export type MaterialTagScope = {
    tag:                {
	id:          number
	name:        string
	category:    Category
    }
    includeDescendants: boolean
}

export type FetchMaterialsParams = {
    q:            string
    tagState:     MaterialIndexTagState
    mediaKind:    MaterialIndexMediaKind
    tagId:        number | null
    includeDescendants: boolean
    groupBy:      MaterialIndexGroupBy
    createdFrom:  string
    createdTo:    string
    updatedFrom:  string
    updatedTo:    string
    sort:         MaterialIndexSort
    direction:    MaterialIndexDirection
    view:         MaterialIndexView
    page:         number
    limit:        number }

export type Material = {
    id:                     number
    versionNo:              number
    tag:                    Tag | null
    file:                   string | null
    url:                    string | null
    sourceKind:             string | null
    sourceUri:              string | null
    sourcePath:             string | null
    sourceFileId:           string | null
    normalizedSourceKey:    string | null
    wikiPageBody?:          string | null
    thumbnail:              string | null
    thumbnailFallbackText:  string | null
    thumbnailFallbackKind:  'tag_name' | 'created_at'
    mediaKind:              'image' | 'video' | 'audio' | 'file_other' | 'url_only'
    contentType:            string | null
    fileByteSize:           number | null
    exportPaths:            Record<string, string>
    exportItems:            MaterialExportItem[]
    createdAt:              string
    createdByUser:          { id: number; name: string }
    updatedAt:              string
    updatedByUser:          { id: number; name: string } }

export type MaterialSyncSuppressionSourceKind =
    | 'uri'
    | 'google_drive_path'
    | 'google_drive_path_prefix'
    | 'google_drive_file'
    | 'legacy_drive_path'
    | 'legacy_drive_path_prefix'

export type MaterialSyncSuppression = {
    id:                  number
    sourceKind:          MaterialSyncSuppressionSourceKind
    sourceUri:           string | null
    drivePath:           string | null
    driveFileId:         string | null
    normalizedSourceKey: string
    reason:              string
    createdByUser:       { id: number; name: string } | null
    createdAt:           string
    updatedAt:           string }

export type MaterialSidebarTag = {
    id:          number
    name:        string
    category:    Category
    deprecated:  boolean
    hasChildren: boolean
    hasMaterial: boolean
    children:    MaterialSidebarTag[] }

export type MaterialTagTree = Omit<Tag, 'children'> & {
    hasChildren: boolean
    hasMaterial: boolean
    children:    MaterialTagTree[]
    material?:   Material | null }

export type MaterialExportItem = {
    id:          number
    profile:     string
    exportPath:  string
    enabled:     boolean }

export type MaterialIndexGroup = {
    key:         string
    tag:         {
	id:          number
	name:        string
	category:    Category
    }
    materialIds: number[]
    count:       number }

export type MaterialIndexResponse = {
    materials: Material[]
    count:     number
    groups?:   MaterialIndexGroup[]
    tagScope?: MaterialTagScope | null
}

export type MaterialVersion = {
    id:               number
    materialId:       number
    versionNo:        number
    eventType:        'create' | 'update' | 'discard' | 'restore'
    tagId:            number | null
    tagName:          string | null
    tagCategory:      Category | null
    url:              string | null
    sourceKind:       string | null
    sourceUri:        string | null
    sourcePath:       string | null
    sourceFileId:     string | null
    normalizedSourceKey: string | null
    fileBlobId:       number | null
    fileFilename:     string | null
    fileContentType:  string | null
    fileByteSize:     number | null
    fileSha256:       string | null
    exportPathsJson:  Record<string, string>
    discardedAt:      string | null
    createdByUser:    { id: number; name: string | null } | null
    createdAt:        string }

export type Menu = MenuItem[]

export type MenuInvisibleItem = {
    name:     ReactNode
    to?:      string
    base?:    string
    visible:  false
    subMenu:  SubMenuItem[] }

export type MenuItem = MenuVisibleItem | MenuInvisibleItem

export type MenuVisibleItem = {
    name:     ReactNode
    to:       string
    base?:    string
    visible?: true
    subMenu:  SubMenuItem[] }

export type NicoTag = Tag & {
    category:               'nico'
    linkedTags:             Tag[]
    recentPostTagCreatedAt: string | null }

export type NiconicoMetadata = {
    currentTime:            number
    duration:               number
    isVideoMetaDataLoaded:  boolean
    maximumBuffered:        number
    muted:                  boolean
    showComment:            boolean
    volume:                 number }

export type NiconicoVideoInfo = {
    title:            string
    videoId:          string
    lengthInSeconds:  number
    thumbnailUrl:     string
    description:      string
    viewCount:        number
    commentCount:     number
    mylistCount:      number
    postedAt:         string
    watchId:          number }

export type NiconicoViewerHandle = {
    play:         () => void
    pause:        () => void
    seek:         (time: number) => void
    mute:         () => void
    unmute:       () => void
    setVolume:    (volume: number) => void
    showComments: () => void
    hideComments: () => void }

export type Platform = typeof PLATFORMS[number]

export type Post = {
    id:                     number
    versionNo:              number
    url:                    string
    title:                  string | null
    thumbnail:              string | null
    thumbnailBase:          string | null
    videoMs:                number | null
    postSimilarityEdges?:   {
			      targetPostId: number
			      cos:          number
			  }[]
    tags:                   TagWithSections[]
    parentPosts?:           Post[]
    childPosts?:            Post[]
    siblingPosts?:          Record<`${ number }`, Post[]>
    viewed:                 boolean
    related:                Post[]
    originalCreatedFrom:    string | null
    originalCreatedBefore:  string | null
    createdAt:              string
    updatedAt:              string
    uploadedUser:           { id: number; name: string | null } | null }

export type PostTagChange = {
    post:       Post
    tag:        Tag | null
    user:       User | null
    changeType: 'add' | 'remove'
    timestamp:  string }

export type PostVersion = {
    postId:                 number
    latestVersionNo:        number
    versionNo:              number
    eventType:              'create' | 'update' | 'discard' | 'restore'
    title:                  { current: string | null; prev: string | null }
    url:                    { current: string; prev: string | null }
    thumbnail:              { current: string | null; prev: string | null }
    thumbnailBase:          { current: string | null; prev: string | null }
    videoMs:                { current: number | null; prev: number | null }
    tags:                   { name: string
			      type: 'context' | 'added' | 'removed' }[]
    parentPosts:            { id: number
			      title: string
			      type: 'context' | 'added' | 'removed' }[]
    originalCreatedFrom:    { current: string | null; prev: string | null }
    originalCreatedBefore:  { current: string | null; prev: string | null }
    createdAt:              string
    createdByUser:          { id: number; name: string | null } | null }

export type SubMenuComponentItem = {
    component:  ReactNode
    visible:    boolean }

export type SubMenuItem = SubMenuComponentItem | SubMenuStringItem

export type SubMenuStringItem = {
    name:     ReactNode
    to:       string
    visible?: boolean }

export type Tag = {
    id:             number
    name:           string
    category:       Category
    deprecatedAt:   string | null
    aliases:        string[]
    parents:        Tag[]
    postCount:      number
    createdAt:      string
    updatedAt:      string
    hasWiki:        boolean
    materialId:     number | null
    hasDeerjikists: boolean
    children?:      Tag[]
    matchedAlias?:  string | null }

export type TagVersion = {
    tagId:                  number
    versionNo:              number
    eventType:              'create' | 'update' | 'discard' | 'restore'
    name:                   { current: string; prev: string | null }
    category:               { current: Category; prev: Category | null }
    deprecatedAt:           { current: string | null; prev: string | null }
    aliases:                { name: string; type: 'context' | 'added' | 'removed' }[]
    parentTags:             { tag: Tag; type: 'context' | 'added' | 'removed' }[]
    createdAt:              string
    createdByUser:          { id: number; name: string | null } | null }

export type TagWithSections = Omit<Tag, 'children'> & { sections: { beginMs:  number
								    endMs:    number | null }[]
					              children: TagWithSections[] }

export type Theatre = {
    id:             number
    name:           string | null
    opensAt:        string
    closesAt:       string | null
    createdByUser:  { id: number; name: string }
    createdAt:      string
    updatedAt:      string }

export type TheatreComment =
    | { theatreId:  number
	no:         number
	deleted:    false
	user:       { id: number, name: string } | null
	content:    string
	createdAt:  string }
    | { theatreId:  number
	no:         number
	deleted:    true
	user:       { id: number, name: string } | null
	content:    null,
	createdAt:  string }

export type TheatreProgramme = {
    theatreId:  number
    position:   number
    post:       Post
    createdAt:  string }

export type TheatreSkipVoteStatus = {
    votesCount:          number
    requiredCount:       number
    watchingUsersCount:  number
    voted:               boolean }

export type TheatreInfo = {
    hostFlg:        boolean
    postId:         number | null
    postStartedAt:  string | null
    postElapsedMs:  number | null
    watchingUsers:  Pick<User, 'id' | 'name'>[]
    skipVote:       TheatreSkipVoteStatus
    skipped?:       boolean }

export type TheatreSkipEvent = {
    id:                 number
    theatreId:          number
    post:               Post
    tags:               Tag[]
    programmePosition:  number | null
    createdAt:          string }

export type TheatrePostWeight = {
    post:     Post
    weight:   number
    penalty:  number
    tags:     Tag[] }

export type TheatreTagPenalty = {
    tag:      Tag
    penalty:  number }

export type TheatrePostSelectionWeights = {
    tagPenalties:  TheatreTagPenalty[]
    lightestPosts: TheatrePostWeight[]
    heaviestPosts: TheatrePostWeight[] }

export type User = {
    id:               number
    name:             string | null
    inheritanceCode:  string
    role:             UserRole }

export type ViewFlagBehavior = typeof ViewFlagBehavior[keyof typeof ViewFlagBehavior]

export type WikiPage = {
    id:             number
    title:          string
    deprecatedAt:   string | null
    createdUserId:  number
    updatedUserId:  number
    createdAt:      string
    updatedAt:      string
    body:           string
    revisionId:     number
    pred:           number | null
    succ:           number | null }

export type WikiPageChange = {
    revisionId: number
    pred:       number | null
    succ:       null
    wikiPage:   Pick<WikiPage, 'id' | 'title' | 'deprecatedAt'>
    user:       Pick<User, 'id' | 'name'>
    kind:       'content' | 'redirect'
    message:    string | null
    timestamp:  string }

export type WikiPageDiff = {
    wikiPageId:       number
    title:            string
    deprecatedAt:     string | null
    olderRevisionId:  number | null
    newerRevisionId:  number
    diff:             WikiPageDiffDiff[] }

export type WikiPageDiffDiff = {
    type:     'context' | 'added' | 'removed'
    content:  string }

export type UserRole = typeof USER_ROLES[number]
