class Semaphore {
	constructor(count) {
		this.count = count
		this.waiting = []
	}
	acquire() {
		return new Promise(resolve => {
			if (this.count > 0) {
				this.count--
				resolve()
			} else {
				this.waiting.push(resolve)
			}
		})
	}
	release() {
		if (this.waiting.length > 0) {
			const resolve = this.waiting.shift()
			resolve()
		} else {
			this.count++
		}
	}
}
let semaphore = new Semaphore(1)
let reader = new FileReader()

const fileToBase64 = (file) => new Promise(async (resolve, reject) => {
	if (!reader) {
		reader = new FileReader()
	}
	await semaphore.acquire()
	reader.readAsDataURL(file)
	reader.onload = () => {
		const dataUrl = reader.result
		if (typeof dataUrl === "string") {
			const base64String = dataUrl.split(',')[1]
			reader.onload = null
			reader.onerror = null
			resolve(base64String)
		} else {
			reader.onload = null
			reader.onerror = null
			reject(new Error('Invalid data URL'))
		}
		semaphore.release()
	}
	reader.onerror = (error) => {
		reader.onload = null
		reader.onerror = null
		reject(error)
		semaphore.release()
	}
})




async function connection(hostname) {
  const isConnected = await chrome.storage.local.get([hostname]);
  let connected = false;
  if (
    isConnected &&
    Object.keys(isConnected).length > 0 &&
    isConnected[hostname]
  ) {
    connected = true;
  }
  return connected;
}

// In your content script
document.addEventListener("qortalExtensionRequests", async (event) => {
  const { type, payload, requestId, timeout } = event.detail; // Capture the requestId
  if (type === "REQUEST_USER_INFO") {
    const hostname = window.location.hostname;
    const res = await connection(hostname);

    if (!res) {
      document.dispatchEvent(
        new CustomEvent("qortalExtensionResponses", {
          detail: {
            type: "USER_INFO",
            data: {
              error: "Not authorized",
            },
            requestId,
          },
        })
      );
      return;
    }
    chrome?.runtime?.sendMessage({ action: "userInfo" }, (response) => {
      if (response.error) {
        document.dispatchEvent(
          new CustomEvent("qortalExtensionResponses", {
            detail: {
              type: "USER_INFO",
              data: {
                error: response.error,
              },
              requestId,
            },
          })
        );
      } else {
        // Include the requestId in the detail when dispatching the response
        document.dispatchEvent(
          new CustomEvent("qortalExtensionResponses", {
            detail: { type: "USER_INFO", data: response, requestId },
          })
        );
      }
    });
  } else if (type === "REQUEST_IS_INSTALLED") {
    chrome?.runtime?.sendMessage({ action: "version" }, (response) => {
      if (response.error) {
        console.error("Error:", response.error);
      } else {
        // Include the requestId in the detail when dispatching the response
        document.dispatchEvent(
          new CustomEvent("qortalExtensionResponses", {
            detail: { type: "IS_INSTALLED", data: response, requestId },
          })
        );
      }
    });
  } else if (type === "REQUEST_CONNECTION") {
    const hostname = window.location.hostname;
    chrome?.runtime?.sendMessage(
      {
        action: "connection",
        payload: {
          hostname,
        },
        timeout,
      },
      (response) => {
        if (response.error) {
          console.error("Error:", response.error);
        } else {
          // Include the requestId in the detail when dispatching the response
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: { type: "CONNECTION", data: response, requestId },
            })
          );
        }
      }
    );
  } else if (type === "REQUEST_OAUTH") {
    const hostname = window.location.hostname;
    const res = await connection(hostname);
    if (!res) {
      document.dispatchEvent(
        new CustomEvent("qortalExtensionResponses", {
          detail: {
            type: "OAUTH",
            data: {
              error: "Not authorized",
            },
            requestId,
          },
        })
      );
      return;
    }

    chrome?.runtime?.sendMessage(
      {
        action: "oauth",
        payload: {
          nodeBaseUrl: payload.nodeBaseUrl,
          senderAddress: payload.senderAddress,
          senderPublicKey: payload.senderPublicKey,
          timestamp: payload.timestamp,
        },
      },
      (response) => {
        if (response.error) {
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: {
                type: "OAUTH",
                data: {
                  error: response.error,
                },
                requestId,
              },
            })
          );
        } else {
          // Include the requestId in the detail when dispatching the response
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: { type: "OAUTH", data: response, requestId },
            })
          );
        }
      }
    );
  } else if (type === "REQUEST_BUY_ORDER") {
    const hostname = window.location.hostname;
    const res = await connection(hostname);
    if (!res) {
      document.dispatchEvent(
        new CustomEvent("qortalExtensionResponses", {
          detail: {
            type: "BUY_ORDER",
            data: {
              error: "Not authorized",
            },
            requestId,
          },
        })
      );
      return;
    }

    chrome?.runtime?.sendMessage(
      {
        action: "buyOrder",
        payload: {
          qortalAtAddresses: payload.qortalAtAddresses,
          hostname,
          useLocal: payload?.useLocal,
        },
        timeout,
      },
      (response) => {
        if (response.error) {
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: {
                type: "BUY_ORDER",
                data: {
                  error: response.error,
                },
                requestId,
              },
            })
          );
        } else {
          // Include the requestId in the detail when dispatching the response
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: { type: "BUY_ORDER", data: response, requestId },
            })
          );
        }
      }
    );
  } else if (type === "REQUEST_LTC_BALANCE") {
    const hostname = window.location.hostname;
    const res = await connection(hostname);
    if (!res) {
      document.dispatchEvent(
        new CustomEvent("qortalExtensionResponses", {
          detail: {
            type: "USER_INFO",
            data: {
              error: "Not authorized",
            },
            requestId,
          },
        })
      );
      return;
    }
    chrome?.runtime?.sendMessage(
      {
        action: "ltcBalance",
        payload: {
          hostname,
        },
        timeout,
      },
      (response) => {
        if (response.error) {
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: {
                type: "LTC_BALANCE",
                data: {
                  error: response.error,
                },
                requestId,
              },
            })
          );
        } else {
          // Include the requestId in the detail when dispatching the response
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: { type: "LTC_BALANCE", data: response, requestId },
            })
          );
        }
      }
    );
  } else if (type === "CHECK_IF_LOCAL") {
    const hostname = window.location.hostname;
    const res = await connection(hostname);
    if (!res) {
      document.dispatchEvent(
        new CustomEvent("qortalExtensionResponses", {
          detail: {
            type: "USER_INFO",
            data: {
              error: "Not authorized",
            },
            requestId,
          },
        })
      );
      return;
    }
    chrome?.runtime?.sendMessage(
      {
        action: "checkLocal",
        payload: {
          hostname,
        },
        timeout,
      },
      (response) => {
        if (response.error) {
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: {
                type: "CHECK_IF_LOCAL",
                data: {
                  error: response.error,
                },
                requestId,
              },
            })
          );
        } else {
          // Include the requestId in the detail when dispatching the response
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: { type: "CHECK_IF_LOCAL", data: response, requestId },
            })
          );
        }
      }
    );
  } else if (type === "REQUEST_AUTHENTICATION") {
    const hostname = window.location.hostname;
    const res = await connection(hostname);
    if (!res) {
      document.dispatchEvent(
        new CustomEvent("qortalExtensionResponses", {
          detail: {
            type: "USER_INFO",
            data: {
              error: "Not authorized",
            },
            requestId,
          },
        })
      );
      return;
    }
    chrome?.runtime?.sendMessage(
      {
        action: "authentication",
        payload: {
          hostname,
        },
        timeout,
      },
      (response) => {
        if (response.error) {
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: {
                type: "AUTHENTICATION",
                data: {
                  error: response.error,
                },
                requestId,
              },
            })
          );
        } else {
          // Include the requestId in the detail when dispatching the response
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: { type: "AUTHENTICATION", data: response, requestId },
            })
          );
        }
      }
    );
  } else if (type === "REQUEST_SEND_QORT") {
    const hostname = window.location.hostname;
    const res = await connection(hostname);
    if (!res) {
      document.dispatchEvent(
        new CustomEvent("qortalExtensionResponses", {
          detail: {
            type: "USER_INFO",
            data: {
              error: "Not authorized",
            },
            requestId,
          },
        })
      );
      return;
    }
    chrome?.runtime?.sendMessage(
      {
        action: "sendQort",
        payload: {
          hostname,
          amount: payload.amount,
          description: payload.description,
          address: payload.address,
        },
        timeout,
      },
      (response) => {
        if (response.error) {
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: {
                type: "SEND_QORT",
                data: {
                  error: response.error,
                },
                requestId,
              },
            })
          );
        } else {
          // Include the requestId in the detail when dispatching the response
          document.dispatchEvent(
            new CustomEvent("qortalExtensionResponses", {
              detail: { type: "SEND_QORT", data: response, requestId },
            })
          );
        }
      }
    );
  } else if (type === "REQUEST_CLOSE_POPUP") {
    const hostname = window.location.hostname;
    const res = await connection(hostname);
    if (!res) {
      document.dispatchEvent(
        new CustomEvent("qortalExtensionResponses", {
          detail: {
            type: "USER_INFO",
            data: {
              error: "Not authorized",
            },
            requestId,
          },
        })
      );
      return;
    }
    chrome?.runtime?.sendMessage({ action: "closePopup" }, (response) => {
      if (response.error) {
        document.dispatchEvent(
          new CustomEvent("qortalExtensionResponses", {
            detail: {
              type: "CLOSE_POPUP",
              data: {
                error: response.error,
              },
              requestId,
            },
          })
        );
      } else {
        // Include the requestId in the detail when dispatching the response
        document.dispatchEvent(
          new CustomEvent("qortalExtensionResponses", {
            detail: { type: "CLOSE_POPUP", data: true, requestId },
          })
        );
      }
    });
  }
  // Handle other request types as needed...
});

async function handleGetFileFromIndexedDB(fileId, sendResponse) {
  try {
    const db = await openIndexedDB();
    const transaction = db.transaction(["files"], "readonly");
    const objectStore = transaction.objectStore("files");

    const getRequest = objectStore.get(fileId);

    getRequest.onsuccess = async function (event) {
      if (getRequest.result) {
        const file = getRequest.result.data;

        try {
          const base64String = await fileToBase64(file);

          // Create a new transaction to delete the file
          const deleteTransaction = db.transaction(["files"], "readwrite");
          const deleteObjectStore = deleteTransaction.objectStore("files");
          const deleteRequest = deleteObjectStore.delete(fileId);

          deleteRequest.onsuccess = function () {
            console.log(`File with ID ${fileId} has been removed from IndexedDB`);
            try {
              sendResponse({ result: base64String });

            } catch (error) {
              console.log('error', error)
            }
          };

          deleteRequest.onerror = function () {
            console.error(`Error deleting file with ID ${fileId} from IndexedDB`);
            sendResponse({ result: null, error: "Failed to delete file from IndexedDB" });
          };
        } catch (error) {
          console.error("Error converting file to Base64:", error);
          sendResponse({ result: null, error: "Failed to convert file to Base64" });
        }
      } else {
        console.error(`File with ID ${fileId} not found in IndexedDB`);
        sendResponse({ result: null, error: "File not found in IndexedDB" });
      }
    };

    getRequest.onerror = function () {
      console.error(`Error retrieving file with ID ${fileId} from IndexedDB`);
      sendResponse({ result: null, error: "Error retrieving file from IndexedDB" });
    };
  } catch (error) {
    console.error("Error opening IndexedDB:", error);
    sendResponse({ result: null, error: "Error opening IndexedDB" });
  } 
}

const testAsync = async (sendResponse)=> {
  await new Promise((res)=> {
    setTimeout(() => {
      res()
    }, 2500);
  })
  sendResponse({ result: null, error: "Testing" });
}

const showSaveFilePicker = async (data) => {
  try {
    const {filename, mimeType,  fileHandleOptions, fileId} = data
    const blob = await retrieveFileFromIndexedDB(fileId)
    const fileHandle = await window.showSaveFilePicker({
        suggestedName: filename,
        types: [
            {
                description: mimeType,
                ...fileHandleOptions
            }
        ]
    })
    const writeFile = async (fileHandle, contents) => {
        const writable = await fileHandle.createWritable()
        await writable.write(contents)
        await writable.close()
    }
    writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
} catch (error) {
    FileSaver.saveAs(blob, filename)
} 
}

chrome.runtime?.onMessage.addListener( function (message, sender, sendResponse) {
  if (message.type === "LOGOUT") {
    // Notify the web page
    window.postMessage(
      {
        type: "LOGOUT",
        from: "qortal",
      },
      "*"
    );
  } else if (message.type === "RESPONSE_FOR_TRADES") {
    // Notify the web page
    window.postMessage(
      {
        type: "RESPONSE_FOR_TRADES",
        from: "qortal",
        payload: message.message,
      },
      "*"
    );
  } else if(message.action === "SHOW_SAVE_FILE_PICKER"){
    showSaveFilePicker(message?.data)
  }

  else  if (message.action === "getFileFromIndexedDB") {
    handleGetFileFromIndexedDB(message.fileId, sendResponse);
    return true; // Keep the message channel open for async response
  }
});

function openIndexedDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open("fileStorageDB", 1);

    request.onupgradeneeded = function (event) {
      const db = event.target.result;
      if (!db.objectStoreNames.contains("files")) {
        db.createObjectStore("files", { keyPath: "id" });
      }
    };

    request.onsuccess = function (event) {
      resolve(event.target.result);
    };

    request.onerror = function () {
      reject("Error opening IndexedDB");
    };
  });
}


async function retrieveFileFromIndexedDB(fileId) {
  const db = await openIndexedDB();
  const transaction = db.transaction(["files"], "readwrite");
  const objectStore = transaction.objectStore("files");

  return new Promise((resolve, reject) => {
    const getRequest = objectStore.get(fileId);

    getRequest.onsuccess = function (event) {
      if (getRequest.result) {
        // File found, resolve it and delete from IndexedDB
        const file = getRequest.result.data;
        objectStore.delete(fileId);
        resolve(file);
      } else {
        reject("File not found in IndexedDB");
      }
    };

    getRequest.onerror = function () {
      reject("Error retrieving file from IndexedDB");
    };
  });
}

async function deleteQortalFilesFromIndexedDB() {
  try {
    console.log("Opening IndexedDB for deleting files...");
    const db = await openIndexedDB();
    const transaction = db.transaction(["files"], "readwrite");
    const objectStore = transaction.objectStore("files");

    // Create a request to get all keys
    const getAllKeysRequest = objectStore.getAllKeys();

    getAllKeysRequest.onsuccess = function (event) {
      const keys = event.target.result;

      // Iterate through keys to find and delete those containing '_qortalfile'
      for (let key of keys) {
        if (key.includes("_qortalfile")) {
          const deleteRequest = objectStore.delete(key);

          deleteRequest.onsuccess = function () {
            console.log(`File with key '${key}' has been deleted from IndexedDB`);
          };

          deleteRequest.onerror = function () {
            console.error(`Failed to delete file with key '${key}' from IndexedDB`);
          };
        }
      }
    };

    getAllKeysRequest.onerror = function () {
      console.error("Failed to retrieve keys from IndexedDB");
    };

    transaction.oncomplete = function () {
      console.log("Transaction complete for deleting files from IndexedDB");
    };

    transaction.onerror = function () {
      console.error("Error occurred during transaction for deleting files");
    };
  } catch (error) {
    console.error("Error opening IndexedDB:", error);
  }
}


async function storeFilesInIndexedDB(obj) {
  // First delete any existing files in IndexedDB with '_qortalfile' in their ID
  await deleteQortalFilesFromIndexedDB();

  // Open the IndexedDB
  const db = await openIndexedDB();
  const transaction = db.transaction(["files"], "readwrite");
  const objectStore = transaction.objectStore("files");

  // Handle the obj.file if it exists and is a File instance
  if (obj.file instanceof File) {
    const fileId = "objFile_qortalfile";

    // Store the file in IndexedDB
    const fileData = {
      id: fileId,
      data: obj.file,
    };
    objectStore.put(fileData);

    // Replace the file object with the file ID in the original object
    obj.fileId = fileId;
    delete obj.file;
  }
  if (obj.blob instanceof Blob) {
    const fileId = "objFile_qortalfile";

    // Store the file in IndexedDB
    const fileData = {
      id: fileId,
      data: obj.blob,
    };
    objectStore.put(fileData);

    // Replace the file object with the file ID in the original object
    let blobObj = {
      type: obj.blob?.type
    }
    obj.fileId = fileId;
    delete obj.blob;
    obj.blob = blobObj
  }

  // Iterate through resources to find files and save them to IndexedDB
  for (let resource of (obj?.resources || [])) {
    if (resource.file instanceof File) {
      const fileId = resource.identifier + "_qortalfile";

      // Store the file in IndexedDB
      const fileData = {
        id: fileId,
        data: resource.file,
      };
      objectStore.put(fileData);

      // Replace the file object with the file ID in the original object
      resource.fileId = fileId;
      delete resource.file;
    }
  }

  // Set transaction completion handlers
  transaction.oncomplete = function () {
    console.log("Files saved successfully to IndexedDB");
  };

  transaction.onerror = function () {
    console.error("Error saving files to IndexedDB");
  };

  return obj; // Updated object with references to stored files
}



const UIQortalRequests = ['GET_USER_ACCOUNT', 'DECRYPT_DATA', 'SEND_COIN', 'GET_LIST_ITEMS', 'ADD_LIST_ITEMS', 'DELETE_LIST_ITEM', 'VOTE_ON_POLL', 'CREATE_POLL', 'SEND_CHAT_MESSAGE', 'JOIN_GROUP', 'DEPLOY_AT', 'GET_USER_WALLET', 'GET_WALLET_BALANCE', 'GET_USER_WALLET_INFO', 'GET_CROSSCHAIN_SERVER_INFO', 'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE', 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY']

if (!window.hasAddedQortalListener) {
  console.log("Listener added");
  window.hasAddedQortalListener = true;
  //qortalRequests
  const listener = async (event) => {
    event.preventDefault();  // Prevent default behavior
    event.stopImmediatePropagation();  // Stop other listeners from firing
  
    // Verify that the message is from the web page and contains expected data
    if (event.source !== window || !event.data || !event.data.action) return;
  
    if (event?.data?.requestedHandler !== 'UI') return;

   await new Promise((res)=> {
    chrome?.runtime?.sendMessage(
      {
        action: "authentication",
        timeout: 60,
      },
      (response) => {
        if (response.error) {
          eventPort.postMessage({
            result: null,
            error: 'User not authenticated',
          });
          res()
          return
        } else {
          res()
        }
      }
    );
   }) 
  
    const sendMessageToRuntime = (message, eventPort) => {
      chrome?.runtime?.sendMessage(message, (response) => {
        if (response.error) {
          eventPort.postMessage({
            result: null,
            error: response,
          });
        } else {
          eventPort.postMessage({
            result: response,
            error: null,
          });
        }
      });
    };
  
    // Check if action is included in the predefined list of UI requests
    if (UIQortalRequests.includes(event.data.action)) {
      sendMessageToRuntime(
        { action: event.data.action, type: 'qortalRequest', payload: event.data },
        event.ports[0]
      );
    } else if (event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' || event?.data?.action === 'PUBLISH_QDN_RESOURCE' || event?.data?.action === 'ENCRYPT_DATA' || event?.data?.action === 'SAVE_FILE') {
      let data;
      try {
        data = await storeFilesInIndexedDB(event.data);
      } catch (error) {
        console.error('Error storing files in IndexedDB:', error);
        event.ports[0].postMessage({
          result: null,
          error: 'Failed to store files in IndexedDB',
        });
        return;
      }
  
      if (data) {
        sendMessageToRuntime(
          { action: event.data.action, type: 'qortalRequest', payload: data },
          event.ports[0]
        );
      } else {
        event.ports[0].postMessage({
          result: null,
          error: 'Failed to prepare data for publishing',
        });
      }
    }
  };
  
  // Add the listener for messages coming from the window
  window.addEventListener('message', listener);
  
}