import React, { useEffect, useState, useReducer } from 'react';
import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import AlbumIcon from '@material-ui/icons/Album';
import * as serverApi from '../../../api/api';
import { WindowState } from '../Windows';
import StoreLinkIcon, { whichStore } from '../../common/StoreLinkIcon';
import TrackTable from '../../tables/ResultsTable';
import { modifyAlbum, modifyTrack } from '../../../lib/saveChanges';
import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { queryAlbums, queryTracks } from '../../../lib/backend/queries';
import { useParams } from 'react-router';
import { handleNotLoggedIn, NotLoggedInError } from '../../../lib/backend/request';
import { useAuth } from '../../../lib/useAuth';
import { Album, Name, Id, StoreLinks, AlbumRefs, Artist, Tag, Track, ResourceType } from '../../../api/api';
import EditItemDialog, { EditablePropertyType } from '../../common/EditItemDialog';
import EditIcon from '@material-ui/icons/Edit';

export type AlbumMetadata = serverApi.QueryResponseAlbumDetails;
export type AlbumMetadataChanges = serverApi.PatchAlbumRequest;

export interface AlbumWindowState extends WindowState {
    id: number,
    metadata: AlbumMetadata | null,
    pendingChanges: AlbumMetadataChanges | null,
    tracksOnAlbum: any[] | null,
}

export enum AlbumWindowStateActions {
    SetMetadata = "SetMetadata",
    SetPendingChanges = "SetPendingChanges",
    SetTracks = "SetTracks",
    Reload = "Reload",
}

export function AlbumWindowReducer(state: AlbumWindowState, action: any) {
    switch (action.type) {
        case AlbumWindowStateActions.SetMetadata:
            return { ...state, metadata: action.value }
        case AlbumWindowStateActions.SetPendingChanges:
            return { ...state, pendingChanges: action.value }
        case AlbumWindowStateActions.SetTracks:
            return { ...state, tracksOnAlbum: action.value }
        case AlbumWindowStateActions.Reload:
            return { ...state, metadata: null, pendingChanges: null, tracksOnAlbum: null }
        default:
            throw new Error("Unimplemented AlbumWindow state update.")
    }
}

export async function getAlbumMetadata(id: number): Promise<AlbumMetadata> {
    let result: any = await queryAlbums(
        {
            a: QueryLeafBy.AlbumId,
            b: id,
            leafOp: QueryLeafOp.Equals,
        }, 0, 1, serverApi.QueryResponseType.Details
    );
    return result[0];
}

export default function AlbumWindow(props: {}) {
    const { id } = useParams<{ id: string }>();
    const [state, dispatch] = useReducer(AlbumWindowReducer, {
        id: parseInt(id),
        metadata: null,
        pendingChanges: null,
        tracksOnAlbum: null,
    });

    return <AlbumWindowControlled state={state} dispatch={dispatch} />
}

export function AlbumWindowControlled(props: {
    state: AlbumWindowState,
    dispatch: (action: any) => void,
}) {
    let { id: albumId, metadata, pendingChanges, tracksOnAlbum } = props.state;
    let { dispatch } = props;
    let auth = useAuth();
    let [editing, setEditing] = useState<boolean>(false);

    // Effect to get the album's metadata.
    useEffect(() => {
        if (metadata === null) {
            getAlbumMetadata(albumId)
                .then((m: AlbumMetadata) => {
                    dispatch({
                        type: AlbumWindowStateActions.SetMetadata,
                        value: m
                    });
                })
                .catch((e: any) => { handleNotLoggedIn(auth, e) })
        }
    }, [albumId, dispatch, metadata]);

    // Effect to get the album's tracks.
    useEffect(() => {
        if (tracksOnAlbum) { return; }

        (async () => {
            const tracks = await queryTracks(
                {
                    a: QueryLeafBy.AlbumId,
                    b: albumId,
                    leafOp: QueryLeafOp.Equals,
                }, 0, -1, serverApi.QueryResponseType.Details
            )
                .catch((e: any) => { handleNotLoggedIn(auth, e) });
            dispatch({
                type: AlbumWindowStateActions.SetTracks,
                value: tracks,
            });
        })();
    }, [tracksOnAlbum, albumId, dispatch]);

    const name = <Typography variant="h4">{metadata?.name || "(Unknown album name)"}</Typography>

    const storeLinks = metadata?.storeLinks && metadata?.storeLinks.map((link: string) => {
        const store = whichStore(link);
        return store && <a
            href={link} target="_blank" rel="noopener noreferrer"
        >
            <IconButton><StoreLinkIcon
                whichStore={store}
                style={{ height: '40px', width: '40px' }}
            />
            </IconButton>
        </a>
    });

    return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap">
        <Box
            m={1}
            mt={4}
            width="80%"
        >
            <AlbumIcon style={{ fontSize: 80 }} />
        </Box>
        <Box
            m={1}
            width="80%"
        >
            {metadata && <Box>
                <Box m={2}>
                    {name}
                </Box>
                <Box m={1}>
                    <Box display="flex" alignItems="center" m={0.5}>
                        {storeLinks}
                    </Box>
                </Box>
                <Box m={1}>
                    <IconButton
                        onClick={() => { setEditing(true); }}
                    ><EditIcon /></IconButton>
                </Box>
            </Box>}
        </Box>
        <Box
            m={1}
            width="80%"
        >
            <Box display="flex" flexDirection="column" alignItems="left">
                <Typography>Tracks in this album in your library:</Typography>
            </Box>
            {props.state.tracksOnAlbum && <TrackTable
                tracks={props.state.tracksOnAlbum}
            />}
            {!props.state.tracksOnAlbum && <CircularProgress />}
        </Box>
        {metadata && <EditItemDialog
            open={editing}
            onClose={() => { setEditing(false); }}
            onSubmit={(v: serverApi.PatchAlbumRequest) => {
                // Remove any details about linked resources and leave only their IDs.
                let v_modified = {
                    ...v,
                    tracks: undefined,
                    artists: undefined,
                    tags: undefined,
                    trackIds: v.trackIds || v.tracks?.map(
                        (a: (Track & Id)) => { return a.id }
                    ) || undefined,
                    artistIds: v.artistIds || v.artists?.map(
                        (a: (Artist & Id)) => { return a.id }
                    ) || undefined,
                    tagIds: v.tagIds || v.tags?.map(
                        (t: (Tag & Id)) => { return t.id }
                    ) || undefined,
                };
                modifyAlbum(albumId, v_modified)
                    .then(() => dispatch({
                        type: AlbumWindowStateActions.Reload
                    }))
            }}
            id={albumId}
            metadata={metadata}
            editableProperties={[
                { metadataKey: 'name', title: 'Name', type: EditablePropertyType.Text },
            ]}
            defaultExternalLinksQuery={metadata.name}
            resourceType={ResourceType.Album}
            editStoreLinks={true}
        />}
    </Box>
}