import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { extname, resolve } from 'path';
import { writeFileSync } from 'fs';
import { v4 as uuidv4 } from 'uuid';

@Injectable()
export class UploadService {
  constructor(
    private prisma: PrismaService
  ) {}

  async getCategories(category_type: string): Promise<{
    document_category_id: number;
    category_type: string;
    position: string;
    category_name_en: string;
    category_name_afr: string;
    hidden: boolean;
  }[]> {
    return await this.prisma.$queryRaw`
      SELECT * FROM document_category
      WHERE category_type = ${category_type}
      ORDER BY CAST(position as unsigned) ASC
    `;
  }

  async getDocumentUploadUUID(): Promise<string> {
    let uuid = 'false'
    while (uuid === 'false') {
      const tryUUID = uuidv4()
      const countOfConflictingUUIDs = await this.prisma.document_upload.count({
        where: { uuid }
      })
      if (countOfConflictingUUIDs === 0) uuid = tryUUID
    }
    return uuid;
  }

  async saveDocumentUpload(
    user_group_id: number,
    user_id: number,
    document_category_id: number,
    file: Express.Multer.File
  ) {
    const uuid = await this.getDocumentUploadUUID();
    const filepath = `${this.getBasePath()}/${uuid}${extname(file.originalname)}`;
    writeFileSync(filepath, file.buffer, 'binary');
    const record = await this.prisma.document_upload.create({
      data: {
        user_group_id,
        user_id,
        document_category_id,
        originalname: file.originalname,
        mimetype: file.mimetype,
        size: file.size,
        uuid
      }
    });
    return record;
  }

  async getDocuments(user_group_id: number, user_id: number) {
    const documents = await this.prisma.document_upload.findMany({
      where: {
        user_group_id,
        user_id
      },
      select: {
        document_upload_id: true,
        user_group_id: true,
        document_category_id: true,
        originalname: true,
        created_at: true
      },
      orderBy: {
        created_at: 'desc'
      }
    });
    const documentCategoryIds = documents.map(d => d.document_category_id);
    const categories =  await this.prisma.document_category.findMany({
      where: {
        document_category_id: {
          in: documentCategoryIds
        }
      },
      select: {
        document_category_id: true,
        category_type: true,
        category_name_en: true,
        category_name_afr: true
      }
    });
    const userGroupIds = documents.map(d => d.user_group_id);
    const groups = await this.prisma.user_group.findMany({
      where: {
        user_group_id: {
          in: userGroupIds
        }
      },
      select: {
        user_group_id: true,
        group_name: true
      }
    });
    return {
      documents,
      categories,
      groups
    }
  }

  /*
   * @description Get all user groups for selection
   * For IPW admins only
   */
  async getAllProfiles() {
    return await this.prisma.user_group.findMany({
      select: {
        user_group_id: true,
        group_name: true,
        bis_id: true
      },
      orderBy: {
        group_name: 'asc'
      }
    });
  }

  /*
   * @description Get all documents that were uploaded
   * For IPW admins only
   */
  async getAllDocuments(user_group_id: number) {
    const documents = await this.prisma.document_upload.findMany({
      where: {
        user_group_id
      },
      select: {
        document_upload_id: true,
        user_group_id: true,
        user_id: true,
        document_category_id: true,
        originalname: true,
        created_at: true
      },
      orderBy: {
        created_at: 'desc'
      }
    });
    const documentCategoryIds = documents.map(d => d.document_category_id);
    const categories =  await this.prisma.document_category.findMany({
      where: {
        document_category_id: {
          in: documentCategoryIds
        }
      },
      select: {
        document_category_id: true,
        category_type: true,
        category_name_en: true,
        category_name_afr: true
      }
    });
    const userGroupIds = documents.map(d => d.user_group_id);
    const groups = await this.prisma.user_group.findMany({
      where: {
        user_group_id: {
          in: userGroupIds
        }
      },
      select: {
        user_group_id: true,
        group_name: true
      }
    });
    const documentUserIds = documents.map(d => d.user_id);
    const users = await this.prisma.users.findMany({
      where: {
        user_id: {
          in: documentUserIds
        }
      },
      select: {
        user_id: true,
        firstname: true,
        lastname: true
      }
    });
    return {
      documents,
      categories,
      groups,
      users
    }
  }

  /*
   * @description Get the full path of a file for download
   * For IPW admins only
   */
  async getDocumentPath(document_upload_id: number) {
    const file = await this.prisma.document_upload.findFirst({
      where: { document_upload_id },
      select: {
        originalname: true,
        uuid: true,
        mimetype: true
      }
    });
    return {
      mimetype: file!.mimetype,
      file: `${this.getBasePath()}/${file!.uuid}${extname(file!.originalname)}`
    };
  }

  /*
   * @description Get the base path of the upload directory
   */
  async getBasePath() {
    return ['staging','production'].includes(process.env.NODE_ENV) ? resolve(__dirname, '..', '..', '..', 'ipw_uploads') : resolve(__dirname, '..', '..', '..', '..', 'ipw_uploads');
  }
}