import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import Channel from "../models/channel.model";
import Source from "../models/source.model";
import { VISIBILITY } from "../types/common.types";
import Category from "../models/category.model";
import { Includeable, WhereOptions } from "sequelize";
import { parseFields, validAttributes, validSort } from "../libs/utils";
import { CustomRequest } from "../types/custom.types";

export default class ChannelController {
    static async create(request: Request, response: Response, next: NextFunction) {
        try {
            const data = request.body || {};
            const logo = (request.files as any)?.logo?.[0]?.filename;
            const banner = (request.files as any)?.banner?.[0]?.filename;
            data.logo = logo || null;
            data.banner = banner || null;
            if (data?.id) delete data.id;
            const result = await Channel.create(data);
            response.status(200).json(result.dataValues);
        } catch (error) {
            next(error);
        }
    }
    static async getAll(request: CustomRequest, response: Response, next: NextFunction) {
        try {
            const query = request?.query;
            const sortFields = parseFields(String(query?.sort));
            const order = validSort(sortFields, Channel.getAttributes());
            const fields = parseFields(String(query?.fields || ''));
            const include: Includeable[] = [];
            const where: WhereOptions<Channel> = {};
            if(!request?.authority) where.status = VISIBILITY.VISIBLE;
            if (Number(query?.category_id)) where.category_id = Number(query.category_id);
            if (fields.includes('Category')) {
                const attributes = parseFields(String(query?.CategoryFields || ''));
                include.push({
                    model: Category,
                    attributes: validAttributes(attributes, Category.getAttributes())
                })
            }
            if (fields.includes('Sources')) {
                const attributes = parseFields(String(query?.SourceFields || ''));
                include.push({
                    model: Source,
                    order: [['rank', 'DESC']],
                    attributes: validAttributes(attributes, Source.getAttributes())
                })
            }
            const result = await Channel.findAll({
                where: where,
                attributes: validAttributes(fields, Channel.getAttributes()),
                include: include,
                order: order || [['rank', 'DESC'], ['createdAt', 'DESC']]
            });
            response.status(200).json(result);
        } catch (error) {
            next(error);
        }
    }
    static async getByID(request: Request, response: Response, next: NextFunction) {
        try {
            const id = request?.params?.id;
            const query = request?.query;
            const fields = parseFields(String(query?.fields));
            const include: Includeable[] = [];
            if (fields.includes('Category')) {
                const attributes = parseFields(String(query?.CategoryFields || ''));
                include.push({
                    model: Category,
                    attributes: validAttributes(attributes, Category.getAttributes())
                })
            }
            if (fields.includes('Sources')) {
                const attributes = parseFields(String(query?.SourceFields || ''));
                include.push({
                    model: Source,
                    where: {
                        status : VISIBILITY.VISIBLE,
                    },
                    attributes: validAttributes(attributes, Source.getAttributes()),
                    required:false,
                })
            }
            const data = await Channel.findByPk(id, {
                attributes: validAttributes(fields, Channel.getAttributes()),
                include: include,
                order: fields.includes('Sources')?[[{ model: Source, as: 'Sources' }, "rank", "DESC"]]:[]
            });
            if (!data?.dataValues) throw createHttpError.NotFound("Channel not found.");
            response.status(200).json(data);
        } catch (error) {
            next(error);
        }
    }
    static async updateById(request: Request, response: Response, next: NextFunction) {
        try {
            const id = request?.params?.id;
            const data = request.body || {};
            const logo = (request.files as any)?.logo?.[0]?.filename;
            const banner = (request.files as any)?.banner?.[0]?.filename;
            if (logo) data.logo = logo || null;
            if (banner) data.banner = banner || null;
            if (data?.id) delete data.id;
            const result = await Channel.findByPk(id);
            if (!result?.dataValues) throw createHttpError.NotFound("Channel not found.")
            else await result.update(data);
            response.status(203).json({
                message: 'Channel updated.'
            });
        } catch (error) {
            next(error);
        }
    }
    static async deleteById(request: Request, response: Response, next: NextFunction) {
        try {
            const id = request?.params?.id;
            await Channel.destroy({
                where: {
                    id: id,
                }
            })
            response.status(203).json({
                message: 'Channel deleted.'
            });
        } catch (error) {
            next(error);
        }
    }
}