import connectDatabase from "../firebase/connectDatabase";
const fs = require("fs");
const prettier = require("prettier");
const readline = require("readline");
const yargs = require("yargs").argv;

export default async (config: any) => {
  const db = connectDatabase();
  const collectionName = yargs._[1] ? yargs._[1].toLowerCase() : null;
  const documentId = yargs.id ? yargs.id : yargs._[2] ? yargs._[2] : null;
  let seedsClonedCount = 0;
  const overwrite = yargs.o || yargs.overwrite || false;
  const seedDir = yargs?.dir || `${process.cwd()}/src/seeds`;

  if (!collectionName && seedDir === `${process.cwd()}/src/seeds`) {
    console.log("Collection name is required to clone a seed!");
    return false;
  }

  async function writeData(filename, data) {
    return new Promise(async (resolve, reject) => {
      const filenameParts: string[] = filename.split("/");
      filenameParts.pop();
      const folderPath = filenameParts.join("/");
      if (!fs.existsSync(folderPath)) {
        fs.mkdirSync(folderPath);
      }

      fs.writeFile(filename, data, { flag: "wx" }, function (err: any) {
        if (err) {
          if (err.code === "EEXIST") {
            if (documentId && !overwrite) {
              console.error("File " + filename + " already exists!");
              const rl = readline.createInterface(
                process.stdin,
                process.stdout
              );
              rl.question("Overwrite? [yes]/no: ", function (answer) {
                if (answer === "no") {
                  console.log("Not overwritting " + filename);
                  rl.close();
                  reject({ message: "Not overwritting" });
                } else {
                  console.log("Overwriting " + filename);
                  fs.writeFile(filename, data);
                  rl.close();
                  resolve({ filename });
                }
              });
            } else {
              fs.writeFileSync(filename, data);
              resolve({ filename });
            }
          } else {
            reject(err);
          }
        } else {
          resolve({ filename });
        }
      });
    });
  }

  async function getDocument() {
    const documentRef = db.collection(collectionName).doc(documentId);
    const document = await documentRef.get();
    if (!document.exists) {
      console.log(
        `No document matching this path: ${collectionName}/${documentId}`
      );
      return false;
    }

    return document.data();
  }

  async function checkForReferences(object: any) {
    const data = object;
    for (const key of Object.keys(object)) {
      const value = object[key];
      if (value && value.constructor && value.constructor.name === "Object") {
        await checkForReferences(value);
      } else if (
        value &&
        value.constructor &&
        value.constructor.name === "DocumentReference"
      ) {
        data[
          key
        ] = `<@db.collection('${data[key]._path.segments[0]}').doc('${data[key]._path.segments[1]}')@> as any`;
      } else if (
        value &&
        value.constructor &&
        value.constructor.name === "Timestamp"
      ) {
        data[
          key
        ] = `<@admin.firestore.Timestamp.fromDate(new Date('${value.toDate()}'))@> as any`;
      } else if (
        value &&
        value.constructor &&
        value.constructor.name === "Array"
      ) {
        const cleanArray: any[] = [];
        for (const item of data[key]) {
          cleanArray.push(await checkForReferences(item));
        }
        data[key] = cleanArray;
      }
    }

    return data;
  }

  async function renderSeed(location, seedContent) {
    return new Promise(async (resolve, reject) => {
      fs.readFile(
        `${process.cwd()}/templates/seed.hbs`,
        "utf8",
        async (customErr, customData) => {
          if (customErr) {
            fs.readFile(
              `${__dirname}/../../templates/seed.hbs`,
              "utf8",
              async (err, data) => {
                if (err) {
                  console.log(err);
                  reject(err);
                }

                try {
                  await writeData(
                    location,
                    await prettier.format(
                      data
                        .replace(
                          /{{modelName}}/g,
                          collectionName.charAt(0).toUpperCase() +
                            collectionName.substring(
                              1,
                              collectionName.length - 1
                            )
                        )
                        .replace(/{{data}}/g, seedContent)
                    )
                  );
                  seedsClonedCount++;
                  resolve(data);
                } catch (err) {
                  reject(err);
                }
              }
            );
          } else {
            try {
              await writeData(
                location,
                await prettier.format(
                  customData
                    .replace(
                      /{{modelName}}/g,
                      collectionName.charAt(0).toUpperCase() +
                        collectionName.substring(1, collectionName.length - 1)
                    )
                    .replace(/{{data}}/g, seedContent)
                )
              );
              seedsClonedCount++;
              resolve(customData);
            } catch (err) {
              reject(err);
            }
          }
        }
      );
    });
  }

  async function getCollection() {
    const collectionRef = await db.collection(collectionName).get();
    if (!collectionRef.docs) {
      console.log(
        `No document matching this path: ${collectionName}/${documentId}`
      );
      return false;
    }

    return collectionRef.docs;
  }

  async function createSeedWithDocumentData(documentData: any, id?: string) {
    const cleanData = await checkForReferences(documentData);
    const seedContent = JSON.stringify(cleanData, null, 2)
      .replace(/"<@/g, "")
      .replace(/@> as any"/g, "")
      .replace(/@>"/g, "");

    return await renderSeed(
      `${seedDir}/${collectionName}/${id ? id : documentId}.ts`,
      seedContent
    );
  }

  if (documentId) {
    const documentData = await getDocument();
    await createSeedWithDocumentData(documentData);
  } else {
    const collectionData = await getCollection();
    for (const document of collectionData || []) {
      await createSeedWithDocumentData(document.data(), document.id);
    }
  }

  console.log(`${seedsClonedCount} seeds cloned successfully!`);
};
