import fs from "fs";
import type { HttpContextContract } from "@ioc:Adonis/Core/HttpContext";
import axios from "axios";
import Env from "@ioc:Adonis/Core/Env";
const path = require("path");

const BASE_URL = Env.get("BASE_URL_LOG");
const BASE_URL_AUTO = `${BASE_URL}/AUTOlog/`;
export default class ErpsController {
  /**
   * Controller do tim cac serial number trong cac log trong khoang thoi gian xac dinh
   * @param {Integer} from thoi gian bat dau YYYYMMDD (vd: 20230130)
   * @param {Integer} to thoi gian ket thuc YYYYMMDD (vd: 20230230)
   */
  public async getIndexSerialNumber({
    request,
    response,
  }: HttpContextContract) {
    const { from, to } = request.all();
    const getListLog = async (from, to) => {
      try {
        const listLog: string[] = [];
        const response = await axios.get(BASE_URL);
        // const responseAUTO = await axios.get(BASE_URL_AUTO);
        console.log("check!!!!!!!!!!!!!!!!!!!!!")

        let data1 = response.data
          .split("\n")
          .filter((i) => i.search("<a href") !== -1 && i.search(".log") !== -1);
        // let data2 = responseAUTO.data
        //   .split("\n")
        //   .filter((i) => i.search("<a href") !== -1 && i.search(".log") !== -1);

        const arrayLine = data1;  

        // const arrayLineAUTO = data
        //   .split("\n")
        //   .filter((i) => i.search("<a href") !== -1 && i.search(".log") !== -1);

        arrayLine.map((u) => {
          let temp = u
            .slice(u.search("<a ") + 9, u.search("</a>"))
            .split(">")[1];

          if (
            parseInt(temp?.split("-")[0]) >= from &&
            parseInt(temp?.split("-")[0]) <= to
          ) {
            listLog.push(
              (BASE_URL + "/") +
                u.slice(u.search("<a ") + 9, u.search("</a>")).split(">")[1] +
                " "
            );
          }
        });
        return listLog;
      } catch (error) {
        console.log(error);
      }
    };

    const fetchWithRetry = async (url) => {
      let retries = 0;
      const MAX_RETRIES = 10;
      while (retries < MAX_RETRIES) {
        try {
          const response = await axios.get(url);
          return response.data;
        } catch (error) {
          if (error.code) {
            retries++;
            continue;
          } else {
            console.error("Error fetching file:", error);
            return null;
          }
        }
      }
      return null;
    };

    const extractInfoFromLine = (line, index, fName, lines) => {
      const cleanedLine = line.replace(/\r/g, "");
      const results: any = [];

      const getWarehouse = (fileName) =>
        /(US(?!A)|-US|\.US|US-)/.test(fileName) ? "US" : "AU";

      const extractSN = (str) =>
        str?.replace(/[!@#$%^&*()_+{}\[\]:;<>,.?~\\/]/g, "").trim();

      // PID & SN
      if (
        cleanedLine.includes("PID:") &&
        cleanedLine.includes("SN:") &&
        !cleanedLine.includes("%")
      ) {
        const parts = cleanedLine.split(",");
        const SN = extractSN(
          parts.find((p) => p.includes("SN:"))?.split(":")[1] ?? ""
        );
        const PID = extractSN(
          parts.find((p) => p.includes("PID:"))?.split(":")[1] ?? ""
        );
        const VID = extractSN(
          parts.find((p) => p.includes("VID:"))?.split(":")[1] ?? ""
        );

        if (SN && SN !== "N/A" && SN.length > 4) {
          results.push({
            PID,
            VID,
            SN,
            line: [index + 1],
            fileName: fName,
            warehouse: getWarehouse(fName),
          });
        }
      }

      // Serial Number
      if (cleanedLine.includes("Serial Number")) {
        const PCB_SN = extractSN(cleanedLine.split(":")[1]);
        if (PCB_SN) {
          results.push({
            PID: "",
            VID: "",
            SN: PCB_SN,
            line: [index + 1],
            fileName: fName,
            warehouse: getWarehouse(fName),
          });
        }
      }

      // Vendor Serial (SN)
      if (cleanedLine.includes("Vendor Serial (SN)")) {
        const PCB_SN = extractSN(cleanedLine.split(":")[1]);
        if (PCB_SN) {
          results.push({
            PID: "",
            VID: "",
            SN: PCB_SN.trim(),
            line: [index + 1],
            fileName: fName,
            warehouse: getWarehouse(fName),
          });
        }
      }

      if (cleanedLine.includes("System serial num")) {
        const PCB_SN = extractSN(cleanedLine.split(":")[1]);
        if (PCB_SN) {
          results.push({
            PID: "",
            VID: "",
            SN: PCB_SN,
            line: [index + 1],
            fileName: fName,
            warehouse: getWarehouse(fName),
          });
        }
      }

      // Processor board ID
      if (cleanedLine.includes("Processor board ID") || cleanedLine.includes("Processor Board ID")) {
        const PBID = extractSN(cleanedLine.split(" ").pop());
        if (PBID?.length >= 8) {
          results.push({
            PID: "",
            VID: "",
            SN: PBID,
            line: [index + 1],
            fileName: fName,
            warehouse: getWarehouse(fName),
          });
        }
      }

      return results;
    };

    const mergeLines = (output, lineResult) => {
      lineResult.forEach((entry) => {
        const existing = output.find((o) => o.SN === entry.SN);
        if (existing) {
          existing.line = [...new Set([...existing.line, ...entry.line])];
          if (entry.PID) existing.PID = entry.PID;
          if (entry.VID) existing.VID = entry.VID;
        } else {
          output.push(entry);
        }
      });
    };

    const fetchFiles = async (from, to) => {
      try {
        const urls = await getListLog(from, to);
        if (!urls || urls.length === 0) {
          console.log("No logs found");
          return [];
        }
        const fileContents = await Promise.all(
          urls.map((u) => fetchWithRetry(u?.split(".log")[0]+".log"))
        );

        let report: any = [];

        fileContents.forEach((content, index) => {
          const lines = content?.split("\n");
          if (!lines) return;

          const fName = path.basename(urls[index] ?? "").trim();
          const output = [];

          lines.forEach((line, i) => {
            const lineResult = extractInfoFromLine(line, i, fName, lines);
            mergeLines(output, lineResult);
          });

          report = [...report, ...output];
        });

        const finalReport = report.filter(
          (i) => i.SN && /^[A-Z0-9-]{5,}$/.test(i.SN)
        );

        fs.writeFileSync(
          "./app/utils/indexSN.txt",
          JSON.stringify(finalReport, null, 2).replace(/\\u0000/g, ""),
          "utf-8"
        );
        console.log("Write loggg !");
        return finalReport;
      } catch (error) {
        console.error("GET INFORMATION FAIL", error);
        throw error;
      }
    };

    const result = await fetchFiles(from, to);
    response.status(200).json(result);
  }

  /**
   * Controller lay noi dung file log theo so dong
   * @param {String} fileName url file log tren server log
   * @param {Integer} line dong chua serial number trong log
   * @param {Integer} range khoang dong truoc/sau line
   * @author {Token} req.headers.authorization //token xac thuc
   */
  public async getParagraph({ request, response }: HttpContextContract) {
    const { fileName, line, range } = request.all();

    try {
      let fName =
        fileName.search("AUTO") !== -1 ? "AUTOlog/" + fileName : fileName;
      const res = await axios.get(BASE_URL + "/" + fName);
      const arrayLine = res?.data?.split("\n");

      if (Number(range) >= Number(line)) {
        response.status(200).json({
          content: arrayLine?.slice(0, line + range)?.join("\n"),
        });
      } else {
        response.status(200).json({
          content: arrayLine?.slice(line - range - 1, line + range)?.join("\n"),
        });
      }
    } catch (error) {
      console.log(error);
      response.status(202).send({ mess: "FILE NOT FOUND", error: error });
    }
  }

  public async store({ request, response }: HttpContextContract) {}

  public async show({}: HttpContextContract) {}

  public async edit({}: HttpContextContract) {}

  public async update({}: HttpContextContract) {}

  public async destroy({}: HttpContextContract) {}
}
