import axios from 'axios';
import { linkSync, mkdirSync, readFileSync, rmSync } from 'fs';
import { basename } from 'path';

import { type Book, type ReadList, type Series } from './types';


// Examples of calling this script:
// $ just l2s copy 0K65Q482KK7SD
// $ just l2s meta 0K65Q482KK7SD
const API = JSON.parse(
    readFileSync(`${process.env.FLAKE}/apps/list2series/.env`, { encoding: 'utf-8' }),
).API;

const LIST_ID = process.argv[3];

const getListInfo = async(): Promise<ReadList> => {
    const res = await axios.request({
        method: 'get',
        maxBodyLength: Infinity,
        url: `https://komga.nelim.org/api/v1/readlists/${LIST_ID}`,
        headers: {
            'Accept': 'application/json',
            'X-API-Key': API,
        },
    });

    return res.data;
};

const getSeries = async(seriesTitle: string, operator = true): Promise<Series[]> => {
    return (await axios.request({
        method: 'post',
        maxBodyLength: Infinity,
        url: 'https://komga.nelim.org/api/v1/series/list?unpaged=true',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-API-Key': API,
        },
        data: JSON.stringify({
            condition: {
                title: {
                    operator: operator ? 'is' : 'isNot',
                    value: seriesTitle,
                },
            },
        }),
    })).data.content;
};

const getSeriesBooks = async(listName: string, seriesPath: string): Promise<Book[]> => {
    const thisSeries = (await getSeries('', false)).find((s) => s.url === seriesPath);

    if (!thisSeries) {
        throw new Error('Series could not be found');
    }

    // Reset Series metadata
    axios.request({
        method: 'patch',
        maxBodyLength: Infinity,
        url: `https://komga.nelim.org/api/v1/series/${thisSeries.id}/metadata`,
        headers: {
            'Content-Type': 'application/json',
            'X-API-Key': API,
        },
        data: JSON.stringify({
            ageRating: null,
            ageRatingLock: true,
            alternateTitles: null,
            alternateTitlesLock: true,
            genres: null,
            genresLock: true,
            language: null,
            languageLock: true,
            links: null,
            linksLock: true,
            publisherLock: true,
            readingDirection: 'LEFT_TO_RIGHT',
            readingDirectionLock: true,
            sharingLabels: null,
            sharingLabelsLock: true,
            status: null,
            statusLock: true,
            summary: null,
            summaryLock: true,
            tags: null,
            tagsLock: true,
            title: listName,
            titleLock: true,
            titleSort: listName,
            titleSortLock: true,
            totalBookCountLock: true,
        }),
    });

    const books = await axios.request({
        method: 'post',
        maxBodyLength: Infinity,
        url: 'https://komga.nelim.org/api/v1/books/list?unpaged=true',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-API-Key': API,
        },
        data: JSON.stringify({
            condition: {
                seriesId: {
                    operator: 'is',
                    value: thisSeries.id,
                },
            },
        }),
    });

    return books.data.content;
};

const getBookInfo = async(id: string): Promise<Book> => {
    const res = await axios.request({
        method: 'get',
        maxBodyLength: Infinity,
        url: `https://komga.nelim.org/api/v1/books/${id}`,
        headers: {
            'Accept': 'application/json',
            'X-API-Key': API,
        },
    });

    return res.data;
};

// There doesn't seem to be a way to wait for the scan to be done
const scanLibrary = (): void => {
    axios.request({
        method: 'post',
        maxBodyLength: Infinity,
        url: 'https://komga.nelim.org/api/v1/libraries/0K4QG58XA29DZ/scan',
        headers: {
            'X-API-Key': API,
        },
    });
};

const setBookMetadata = async(i: number, source: Book, target: Book): Promise<void> => {
    const thisSeries = (await getSeries(source.seriesTitle))[0];

    source.metadata.title = thisSeries.booksCount !== 1 ?
        `${source.seriesTitle} Issue #${source.metadata.number}` :
        source.metadata.title = source.seriesTitle;

    source.metadata.number = i.toString();
    source.metadata.numberSort = i;

    const metadata = JSON.stringify(source.metadata);

    axios.request({
        method: 'patch',
        maxBodyLength: Infinity,
        url: `https://komga.nelim.org/api/v1/books/${target.id}/metadata`,
        headers: {
            'Content-Type': 'application/json',
            'X-API-Key': API,
        },
        data: metadata,
    });
};

const main = async(): Promise<void> => {
    const list = await getListInfo();
    const ids = list.bookIds;
    const seriesPath = `/data/comics/[List] ${list.name}`;

    const listBooks = [] as Book[];

    for (let i = 0; i < ids.length; i++) {
        const book = await getBookInfo(ids[i]);

        listBooks[i] = book;
    };

    if (process.argv[2] === 'copy') {
        rmSync(seriesPath, { recursive: true, force: true });
        mkdirSync(seriesPath, { recursive: true });

        for (const book of listBooks) {
            const bookPath = book.url;
            const inListPath = `${seriesPath}/${basename(bookPath)}`;

            console.log(`hardlinking ${basename(bookPath)}`);
            linkSync(bookPath, inListPath);
        }

        scanLibrary();
    }

    else if (process.argv[2] === 'meta') {
        const seriesBooks = await getSeriesBooks(`[List] ${list.name}`, seriesPath);

        for (const target of seriesBooks) {
            const source = listBooks.find((b) => basename(b.url) === basename(target.url));

            if (source) {
                const i = listBooks.indexOf(source) + 1;

                console.log(`Setting metadata for ${source.name}`);
                setBookMetadata(i, source, target);
            }
        }
    }
};

main();