import { ipcMain } from 'electron';
import { app, BrowserWindow } from 'electron';
import { EVENT_NAMES } from '../../constants/define';
import BaseGenerator from '../code-generator/base-generator';
import { findLast } from 'lodash';

const path = require('path');
const puppeteer = require('puppeteer-core');
const {
  ON_CLOSE_RECORDER,
  ON_UPDATE_DETAIL_SCRAPE_COLUMN,
  ON_UPDATE_EVENT_LOG,
  ON_UPDATE_SELECTOR,
  RECORD,
  REPLAY,
  PAUSE,
  RESUME,
  INDICATE_SELECTOR,
  DETAIL_COLUMN_TABLE_RECORD,
  STOP,
} = EVENT_NAMES.CLIENT_RECORDER;

export default class InitRecorder {
  constructor({ pie, mainWindow, recorder, tracker }) {
    this.mainWindow = mainWindow;
    this.pie = pie;
    this.page, this.browserWindow;
    // this.loader = loader;
    this.recorder = recorder;
    this.tracker = tracker;
  }

  async init() {
    ipcMain.on(RECORD, this._handleRecorder);
    ipcMain.on(REPLAY, this._handleReplay);
    ipcMain.on(INDICATE_SELECTOR, this._handleIndicateSelector);
    ipcMain.on(DETAIL_COLUMN_TABLE_RECORD, this._handleRecordDetailColumn);
    ipcMain.on(STOP, this._stop);
    ipcMain.on(PAUSE, this._pause);
    ipcMain.on(RESUME, this._resume);
  }

  _createWindow = async () => {
    try {
      this._stop();
      const browser = await this.pie.connect(app, puppeteer);

      const browserWindow = new BrowserWindow({
        width: 1300,
        height: 1000,
        webPreferences: {
          preload: path.join(__dirname, 'preload2.js'),
        },
      });
      const page = await this.pie.getPage(browser, browserWindow);

      this.page = page;
      this.browserWindow = browserWindow;

      await page.once('domcontentloaded', async () => {
        await page.evaluate(() => {
          // window.headlessRecorder?.initLoader();
          localStorage.clear();
        });
      });

      // first loading when initial browser
      await page.evaluate(async () => {
        await window.headlessRecorder?.initLoader();
      });

      if (process.env.NODE_ENV === 'development') {
        browserWindow.webContents.openDevTools();
      }

      this.page.on('close', async () => {
        this.page = null;
        this.browserWindow = null;
        page.removeAllListeners();
        this.mainWindow.webContents.send(ON_CLOSE_RECORDER, 1);
      });

      ipcMain.on(EVENT_NAMES.COMMON.CLOSE_WINDOW, async () => {
        await this._stop();
      });
    } catch (error) {
      console.log("Can't create window: ", error);
    }
  };

  _handleRecorder = async (_, url) => {
    try {
      await this._createWindow();

      let eventLogs = [];

      await this.page.exposeFunction('onUpdateEvents', (newEventLog) => {
        this.mainWindow.webContents.send(ON_UPDATE_EVENT_LOG, newEventLog);
        eventLogs = newEventLog;
      });

      this.page.on('domcontentloaded', async () => {
        await this.page.evaluate((eventLog) => {
          window.headlessRecorder.record({
            options: {
              recording: eventLog,
            },
            onUpdateEvents: window.onUpdateEvents,
          });
        }, eventLogs);
      });

      await this.page.goto(url, { waitUntil: 'networkidle0' });
    } catch (error) {
      console.log('handleRecorder error: ', error);
    }
  };

  _handleReplay = async (_, eventLogs, stepId, isPreview = true, callback) => {
    try {
      if (stepId) {
        eventLogs = eventLogs.filter((eventLog) => {
          if (isPreview) return eventLog.id <= stepId;
          return eventLog.id < stepId;
        });
      }

      await this._createWindow();

      // loading every page
      this.page.on('domcontentloaded', async () => {
        await this.page.evaluate(async () => {
          await window.headlessRecorder.initLoader();
        });
      });

      callback && (await callback());

      const baseGenerator = new BaseGenerator();
      let code = baseGenerator._parseEvents(eventLogs);
      const wrappedHeader = `await (async () => {\n
        try {`;
      const wrappedFooter = `\n
      } catch (error) {
        console.log('Error at replay ', { error });
      }
})()`;
      code = wrappedHeader + code + wrappedFooter;
      // const func = new Function('page', code);
      const func = new Function('page', 'return (async () => {' + code + '})()');
      await func(this.page);

      return this.page;
    } catch (error) {
      console.log('handleReplay error: ', error);
    }
  };

  _handleIndicateSelector = async (_, eventLogs, stepId) => {
    try {
      eventLogs = eventLogs.filter((eventLog) => {
        return eventLog.id <= stepId;
      });

      const callback = async () => {
        await this.page.exposeFunction('onUpdateSelector', (type, selector, store) => {
          this.mainWindow.webContents.send(ON_UPDATE_SELECTOR, type, selector, store);
        });

        this.page.on('domcontentloaded', async () => {
          await this.page.evaluate(
            async (eventLogs, stepId) => {
              await window.headlessRecorder.reselectSelector({
                eventLogs,
                stepId,
                onUpdateSelector: window?.onUpdateSelector,
              });
            },
            eventLogs,
            stepId,
          );
        });
      };

      await this._handleReplay(_, eventLogs, stepId, false, callback);
    } catch (error) {
      console.log('handleIndicateSelector error: ', error);
    }
  };

  _handleRecordDetailColumn = async (_, { eventLogs, stepId, linkColumnPosition }) => {
    try {
      eventLogs = eventLogs.filter((eventLog) => {
        return eventLog.id <= stepId;
      });

      const callback = async () => {
        await this.page.exposeFunction('onUpdateRecordScrapingDetailColumn', (store) => {
          this.mainWindow.webContents.send(ON_UPDATE_DETAIL_SCRAPE_COLUMN, store);
        });

        this.page.on('domcontentloaded', async () => {
          await this.page.evaluate(async () => {
            await window.headlessRecorder.recordDetailColumn({
              onUpdateRecordScrapingDetailColumn: window?.onUpdateRecordScrapingDetailColumn,
            });
          });
        });
      };

      await this._handleReplay(_, eventLogs, stepId, true, callback);

      const lastExtractedData = findLast(eventLogs, (eventLog) => eventLog.action === 'extract-data');
      if (lastExtractedData?.selector) {
        // Extract table data from the selector
        const tableData = await this.page.$eval(
          lastExtractedData.selector,
          (table, linkColumnPosition) => {
            // Replace columnIndex with linkColumnPosition
            const rows = Array.from(table.querySelectorAll('tr'));
            const columnData = rows.map((row) => {
              const linkElement = row.querySelector(`td:nth-child(${linkColumnPosition}) a`); // Select the 'a' element inside the cell
              return linkElement ? linkElement.href : null; // Get the 'href' attribute of the 'a' element
            });
            return columnData.find((value) => value !== null);
          },
          linkColumnPosition,
        );

        this.page.goto(tableData, { waitUntil: 'networkidle0' });
      }
    } catch (error) {
      console.log('handleRecordDetailCollumn error: ', error);
    }
  };

  _stop = async () => {
    try {
      if (this?.page && !this.page.isClosed()) {
        await this.page.close();
      }
      if (this?.browserWindow && !this.browserWindow.isDestroyed()) {
        this.browserWindow.close();
      }
    } catch (error) {
      console.log('stop error: ', error);
    }
  };

  _pause = async () => {
    if (this?.page && !this.page.isClosed()) {
      await this.page.evaluate(async () => {
        await window.headlessRecorderAPI.pause();
      });
    }
  };

  _resume = async () => {
    if (this?.page && !this.page.isClosed()) {
      await this.page.evaluate(async () => {
        await window.headlessRecorderAPI.resume();
      });
    }
  };
}
