import * as plugins from '../typedserver.plugins.js';
import * as interfaces from '../interfaces/index.js';

import { Handler } from './classes.handler.js';

export class HandlerStatic extends Handler {
  constructor(
    pathArg: string,
    optionsArg?: {
      requestModifier?: interfaces.TRequestModifier;
      responseModifier?: interfaces.TResponseModifier;
      headers?: { [key: string]: string };
      serveIndexHtmlDefault?: boolean;
    }
  ) {
    super('GET', async (req, res) => {
      let requestPath = req.path;
      let requestHeaders = req.headers;
      let requestBody = req.body;
      let travelData: unknown;
      if (optionsArg && optionsArg.requestModifier) {
        const modifiedRequest = await optionsArg.requestModifier({
          headers: requestHeaders,
          path: requestPath,
          body: requestBody,
        });

        requestHeaders = modifiedRequest.headers;
        requestPath = modifiedRequest.path;
        requestBody = modifiedRequest.body;
        travelData = modifiedRequest.travelData;
      }

      // lets compute some paths
      let filePath: string = requestPath.slice(req.route.path.length - 1); // lets slice of the root
      if (requestPath === '') {
        console.log('replaced root with index.html');
        filePath = 'index.html';
      }
      console.log(filePath);
      const joinedPath = plugins.path.join(pathArg, filePath);
      const defaultPath = plugins.path.join(pathArg, 'index.html');
      let parsedPath = plugins.path.parse(joinedPath);
      let usedPath: string;

      // important security checks
      if (
        requestPath.includes('..') || // don't allow going up the filePath
        requestPath.includes('~') || // don't allow referencing of home directory
        !joinedPath.startsWith(pathArg) // make sure the joined path is within the directory
      ) {
        res.writeHead(500);
        res.end();
        return;
      }

      // set additional headers
      if (optionsArg && optionsArg.headers) {
        for (const key of Object.keys(optionsArg.headers)) {
          res.set(key, optionsArg.headers[key]);
        }
      }

      // lets actually care about serving, if security checks pass
      let fileString: string;
      let fileEncoding: 'binary' | 'utf8';
      try {
        fileString = plugins.smartfile.fs.toStringSync(joinedPath);
        fileEncoding = plugins.smartmime.getEncoding(joinedPath);
        usedPath = joinedPath;
      } catch (err) {
        // try serving index.html instead
        console.log(`could not resolve ${joinedPath}`);
        if (optionsArg && optionsArg.serveIndexHtmlDefault) {
          console.log(`serving default path ${defaultPath} instead of ${joinedPath}`);
          try {
            parsedPath = plugins.path.parse(defaultPath);
            fileString = plugins.smartfile.fs.toStringSync(defaultPath);
            fileEncoding = plugins.smartmime.getEncoding(defaultPath);
            usedPath = defaultPath;
          } catch (err) {
            res.writeHead(500);
            res.end('File not found!');
            return;
          }
        } else {
          res.writeHead(500);
          res.end('File not found!');
          return;
        }
      }

      res.type(parsedPath.ext);

      const headers = res.getHeaders();

      // lets modify the response at last
      if (optionsArg && optionsArg.responseModifier) {
        const modifiedResponse = await optionsArg.responseModifier({
          headers: res.getHeaders(),
          path: usedPath,
          responseContent: fileString,
          travelData,
        });

        // headers
        for (const key of Object.keys(res.getHeaders())) {
          if (!modifiedResponse.headers[key]) {
            res.removeHeader(key);
          }
        }

        for (const key of Object.keys(modifiedResponse.headers)) {
          res.setHeader(key, modifiedResponse.headers[key]);
        }

        // responseContent
        fileString = modifiedResponse.responseContent;
      }

      res.status(200);
      res.write(Buffer.from(fileString, fileEncoding));
      res.end();
    });
  }
}