const express = require('express');
const router = express.Router();

import pool from '../db';
import { approveApplication, createApplication, denyApplication, getAllMemberApplications, getApplicationByID, getApplicationComments, getApplicationList, getMemberApplication } from '../services/db/applicationService';
import { setUserState } from '../services/db/memberService';
import { MemberState } from '@app/shared/types/member';
import { getRankByName, insertMemberRank } from '../services/db/rankService';
import { ApplicationFull, CommentRow } from "@app/shared/types/application"
import { assignUserToStatus } from '../services/db/statusService';
import { Request, response, Response } from 'express';
import { getUserRoles } from '../services/db/rolesService';
import { requireLogin, requireRole } from '../middleware/auth';
import { logger } from '../services/logging/logger';
import { bus } from '../services/events/eventBus';

//get CoC
router.get('/coc', async (req: Request, res: Response) => {
  try {
    const response = await fetch(`${process.env.DOC_HOST}/api/pages/714`, {
      headers: {
        Authorization: `Token ${process.env.DOC_TOKEN_ID}:${process.env.DOC_TOKEN_SECRET}`,
      },
    });

    if (!response.ok) {
      const text = await response.text();
      logger.error('app', 'Failed to fetch LOA policy from Bookstack', {
        status: response.status,
        statusText: response.statusText,
        body: text,
      });
      return res.sendStatus(500);
    }

    const out = await response.json();
    res.status(200).json(out.html);

  } catch (error) {
    logger.error('app', 'Error fetching LOA policy from Bookstack', {
      error: error instanceof Error ? error.message : String(error),
    });
    res.sendStatus(500);
  }
});


// POST /application
router.post('/', [requireLogin], async (req: Request, res: Response) => {
  const memberID = req.user.id;
  const App = req.body?.App || {};
  const appVersion = 1;

  try {
    let appID = await createApplication(memberID, appVersion, JSON.stringify(App));

    await setUserState(memberID, MemberState.Applicant);

    res.sendStatus(201);

    bus.emit("application.create", { application: appID, member_name: req.user.name, member_discord_id: req.user.discord_id || null })

    logger.info('app', 'Application Posted', {
      user: memberID,
      app: appID
    })
  } catch (err) {
    logger.error(
      'app',
      'Failed to create application',
      {
        memberID,
        error: err instanceof Error ? err.message : String(err),
        stack: err instanceof Error ? err.stack : undefined,
      }
    );
    res.status(500).json({ error: 'Failed to create application' });
  }
});


// GET /application/all
router.get('/all', [requireLogin, requireRole("Recruiter")], async (req, res) => {
  try {
    const rows = await getApplicationList();
    res.status(200).json(rows);
  } catch (err) {
    logger.error(
      'app',
      'Failed to get applications',
      {
        error: err instanceof Error ? err.message : String(err),
        stack: err instanceof Error ? err.stack : undefined,
      }
    );
    res.status(500);
  }
});

router.get('/meList', async (req, res) => {

  let userID = req.user.id;

  try {
    let application = await getAllMemberApplications(userID);

    return res.status(200).json(application);
  } catch (error) {
    logger.error(
      'app',
      'Failed to get applications for user',
      {
        user: userID,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    return res.status(500);
  }
})

router.get('/me', [requireLogin], async (req, res) => {

  let userID = req.user.id;

  try {
    let application = await getMemberApplication(userID);

    if (application === undefined) {
      res.sendStatus(204);
      return;
    }

    const comments: CommentRow[] = await getApplicationComments(application.id);

    const output: ApplicationFull = {
      application,
      comments,
    }

    return res.status(200).json(output);
  } catch (error) {
    logger.error(
      'app',
      'Failed to load application',
      {
        user: userID,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    return res.status(500).json(error);
  }
})

// GET /me/:id
router.get('/me/:id', [requireLogin], async (req: Request, res: Response) => {
  let appID = Number(req.params.id);
  let member = req.user.id;
  try {
    const application = await getApplicationByID(appID);
    if (application === undefined)
      return res.sendStatus(204);
    if (application.member_id != member) {
      return res.sendStatus(403);
    }

    const comments: CommentRow[] = await getApplicationComments(appID);

    const output: ApplicationFull = {
      application,
      comments,
    }
    return res.status(200).json(output);
  }
  catch (error) {
    logger.error(
      'app',
      'Failed to load application',
      {
        application: appID,
        user: member,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    return res.status(500);
  }
});

// GET /application/:id
router.get('/:id', [requireLogin, requireRole("Recruiter")], async (req: Request, res: Response) => {
  let appID = Number(req.params.id);
  let asAdmin = !!req.query.admin || false;

  try {
    const application = await getApplicationByID(appID);
    if (application === undefined)
      return res.sendStatus(204);

    const comments: CommentRow[] = await getApplicationComments(appID, asAdmin);

    const output: ApplicationFull = {
      application,
      comments,
    }
    return res.status(200).json(output);
  }
  catch (error) {
    logger.error(
      'app',
      'Failed to load application',
      {
        application: appID,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    return res.status(500);
  }
});

// POST /application/approve/:id
router.post('/approve/:id', [requireLogin, requireRole("Recruiter")], async (req: Request, res: Response) => {
  const appID = Number(req.params.id);
  const approved_by = req.user.id;

  try {
    const app = await getApplicationByID(appID);
    await approveApplication(appID, approved_by);

    //update user profile
    await setUserState(app.member_id, MemberState.Member);

    await pool.query('CALL sp_accept_new_recruit_validation(?, ?, ?, ?)', [Number(process.env.CONFIG_ID), app.member_id, approved_by, approved_by])

    logger.info('app', "Member application approved", {
      application: app.id,
      applicant: app.member_id,
      approver: approved_by
    })
    res.sendStatus(200);
  } catch (error) {
    logger.error(
      'app',
      'Failed to approve application',
      {
        application: appID,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    res.status(500).json({ error: 'Failed to approve application' });
  }
});

// POST /application/deny/:id
router.post('/deny/:id', [requireLogin, requireRole("Recruiter")], async (req: Request, res: Response) => {
  const appID = Number(req.params.id);
  const approver = Number(req.user.id);

  try {
    const app = await getApplicationByID(appID);
    await denyApplication(appID, approver);
    await setUserState(app.member_id, MemberState.Denied);

    logger.info('app', "Member application approved", {
      application: app.id,
      applicant: app.member_id,
      approver: approver
    })
    res.sendStatus(200);
  } catch (error) {
    logger.error(
      'app',
      'Failed to deny application',
      {
        application: appID,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    res.status(500).json({ error: 'Failed to deny application' });
  }
});

// POST /application/:id/comment
router.post('/:id/comment', [requireLogin], async (req: Request, res: Response) => {
  const appID = req.params.id;
  const data = req.body.message;
  const user = req.user;

  const sql = `INSERT INTO application_comments(
    application_id,
    poster_id,
    post_content
  )
VALUES(?, ?, ?);`


  try {
    var conn = await pool.getConnection();

    const result = await conn.query(sql, [appID, user.id, data])
    if (result.affectedRows !== 1) {
      conn.release();
      throw new Error("Insert Failure")
    }

    const getSQL = `SELECT app.id AS comment_id,
      app.post_content,
      app.poster_id,
      app.post_time,
      app.last_modified,
      member.name AS poster_name
        FROM application_comments AS app
          INNER JOIN members AS member ON member.id = app.poster_id
        WHERE app.id = ?; `;
    const comment = await conn.query(getSQL, [result.insertId])

    logger.info('app', "Application comment posted", {
      application: appID,
      poster: user.id,
      comment: result.insertId,
    })

    res.status(201).json(comment[0]);

  } catch (error) {
    logger.error(
      'app',
      'Failed to post comment',
      {
        application: appID,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    res.status(500).json({ error: 'Could not post comment' });
  } finally {
    conn.release();
  }
});

// POST /application/:id/comment
router.post('/:id/adminComment', [requireLogin, requireRole("Recruiter")], async (req: Request, res: Response) => {
  const appID = req.params.id;
  const data = req.body.message;
  const user = req.user;

  const sql = `INSERT INTO application_comments(
    application_id,
    poster_id,
    post_content,
    admin_only
  )
VALUES(?, ?, ?, 1);`

  try {
    var conn = await pool.getConnection();

    const result = await conn.query(sql, [appID, user.id, data])
    if (result.affectedRows !== 1) {
      conn.release();
      throw new Error("Insert Failure")
    }

    const getSQL = `SELECT app.id AS comment_id,
      app.post_content,
      app.poster_id,
      app.post_time,
      app.last_modified,
      app.admin_only,
      member.name AS poster_name
        FROM application_comments AS app
          INNER JOIN members AS member ON member.id = app.poster_id
        WHERE app.id = ?; `;
    const comment = await conn.query(getSQL, [result.insertId])

    logger.info('app', "Admin application comment posted", {
      application: appID,
      poster: user.id,
      comment: result.insertId,
    })

    res.status(201).json(comment[0]);
  } catch (error) {
    logger.error(
      'app',
      'Failed to post comment',
      {
        application: appID,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    res.status(500).json({ error: 'Could not post comment' });
  } finally {
    conn.release();
  }
});

router.post('/restart', async (req: Request, res: Response) => {
  const user = req.user.id;
  try {
    await setUserState(user, MemberState.Guest);

    logger.info('app', "Member restarted application", {
      user: user
    })

    res.sendStatus(200);
  } catch (error) {
    logger.error(
      'app',
      'Failed to restart application',
      {
        user: user,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      }
    );
    res.status(500).json({ error: 'Could not rester application' });
  }
})

export const applicationRouter = router;
