import {FastifyReply, FastifyRequest} from 'fastify';
import fs from 'fs';
import * as service from './local.service';

type ScanQuery = {
    mode?: 'full' | 'incremental';
};

type Params = {
    type: 'anime' | 'manga' | 'novels';
    id?: string;
};

type MatchBody = {
    source: 'anilist';
    matched_id: number | null;
};

export async function scanLibrary(request: FastifyRequest<{ Querystring: ScanQuery }>, reply: FastifyReply) {
    try {
        const mode = request.query.mode || 'incremental';
        return await service.performLibraryScan(mode);
    } catch (err: any) {
        if (err.message === 'NO_LIBRARY_CONFIGURED') {
            return reply.status(400).send({ error: 'NO_LIBRARY_CONFIGURED' });
        }
        return reply.status(500).send({ error: 'FAILED_TO_SCAN_LIBRARY' });
    }
}

export async function listEntries(request: FastifyRequest<{ Params: Params }>, reply: FastifyReply) {
    try {
        const { type } = request.params;
        const entries = await service.getEntriesByType(type);
        return entries;
    } catch {
        return reply.status(500).send({ error: 'FAILED_TO_LIST_ENTRIES' });
    }
}

export async function getEntry(request: FastifyRequest<{ Params: Params }>, reply: FastifyReply) {
    try {
        const { type, id } = request.params as { type: string, id: string };
        const entry = await service.getEntryDetails(type, id);

        if (!entry) {
            return reply.status(404).send({ error: 'ENTRY_NOT_FOUND' });
        }

        return entry;
    } catch {
        return reply.status(500).send({ error: 'FAILED_TO_GET_ENTRY' });
    }
}

export async function streamUnit(request: FastifyRequest, reply: FastifyReply) {
    const { id, unit } = request.params as any;

    const fileInfo = await service.getFileForStreaming(id, unit);

    if (!fileInfo) {
        return reply.status(404).send({ error: 'FILE_NOT_FOUND' });
    }

    const { filePath, stat } = fileInfo;
    const range = request.headers.range;

    if (!range) {
        reply
            .header('Content-Length', stat.size)
            .header('Content-Type', 'video/mp4');
        return fs.createReadStream(filePath);
    }

    const parts = range.replace(/bytes=/, '').split('-');
    const start = Number(parts[0]);
    const end = parts[1] ? Number(parts[1]) : stat.size - 1;

    if (
        Number.isNaN(start) ||
        Number.isNaN(end) ||
        start < 0 ||
        start >= stat.size ||
        end < start ||
        end >= stat.size
    ) {
        return reply.status(416).send({ error: 'INVALID_RANGE' });
    }

    const contentLength = end - start + 1;

    reply
        .status(206)
        .header('Content-Range', `bytes ${start}-${end}/${stat.size}`)
        .header('Accept-Ranges', 'bytes')
        .header('Content-Length', contentLength)
        .header('Content-Type', 'video/mp4');

    return fs.createReadStream(filePath, { start, end });
}

export async function matchEntry(
    request: FastifyRequest<{ Body: MatchBody }>,
    reply: FastifyReply
) {
    const { id, type } = request.params as any;
    const { source, matched_id } = request.body;

    const result = await service.updateEntryMatch(id, type, source, matched_id);

    if (!result) {
        return reply.status(404).send({ error: 'ENTRY_NOT_FOUND' });
    }

    return result;
}

export async function getUnits(request: FastifyRequest<{ Params: Params }>, reply: FastifyReply) {
    try {
        const { id } = request.params as { id: string };
        const units = await service.getEntryUnits(id);

        if (!units) {
            return reply.status(404).send({ error: 'ENTRY_NOT_FOUND' });
        }

        return units;
    } catch (err) {
        console.error('Error getting units:', err);
        return reply.status(500).send({ error: 'FAILED_TO_GET_UNITS' });
    }
}

export async function getManifest(request: FastifyRequest, reply: FastifyReply) {
    const { unitId } = request.params as any;

    try {
        const manifest = await service.getUnitManifest(unitId);

        if (!manifest) {
            return reply.status(404).send({ error: 'FILE_NOT_FOUND' });
        }

        return manifest;
    } catch (err: any) {
        if (err.message === 'UNSUPPORTED_FORMAT') {
            return reply.status(400).send({ error: 'UNSUPPORTED_FORMAT' });
        }
        return reply.status(500).send({ error: 'FAILED_TO_GET_MANIFEST' });
    }
}

export async function getPage(request: FastifyRequest, reply: FastifyReply) {
    const { unitId, resId } = request.params as any;

    const resource = await service.getUnitResource(unitId, resId);

    if (!resource) {
        return reply.status(404).send();
    }

    if (resource.type === 'image') {
        if (resource.data) {
            return reply
                .header('Content-Type', 'image/jpeg')
                .send(resource.data);
        }

        if (resource.path && resource.size) {
            reply
                .header('Content-Length', resource.size)
                .header('Content-Type', 'image/jpeg');

            return fs.createReadStream(resource.path);
        }
    }

    if (resource.type === 'html') {
        return reply
            .header('Content-Type', 'text/html; charset=utf-8')
            .send(resource.data);
    }

    return reply.status(400).send();
}