// initialize required packages
const fs = require('fs');
const ini = require('ini');
const nostr = require('nostr-tools');
const os = require('os');
const path = require('path');
const wspf = require('websocket-polyfill');
const WebSocket = require('ws');

const { client, xml } = require('@xmpp/client');

// nostr things
let {
  bech32,
  bech32m
} = require('bech32');

let letsCID;
let signedEvent;

// config setup for home folders
// Determine the platform-specific config path
function getConfigPath() {
    const platform = os.platform();
    if (platform === 'win32') {
        // Windows
        return path.join(process.env.APPDATA, 'nostrsms', 'config.ini');
    } else if (platform === 'darwin') {
        // macOS
        return path.join(os.homedir(), 'Library', 'Application Support', 'nostrsms', 'config.ini');
    } else {
        // Linux and other Unix-like systems
        return path.join(os.homedir(), '.config', 'nostrsms', 'config.ini');
    }
}

// prepare config for parsing
const configPath = getConfigPath();
if (fs.existsSync(configPath)) {
  console.log(`Found config at ${configPath}`);
  const config = ini.parse(fs.readFileSync(configPath, 'utf-8'));
  let sk = config.nostr.pkhex;
  let pk = nostr.getPublicKey(sk);

  // prepare xmpp client
  const xmpp = client({
    service: config.xmpp.host,
    domain: config.xmpp.domain,
    resource: config.xmpp.resource || 'bot',
    username: config.xmpp.user,
    password: config.xmpp.password,
  });
  
  // set up allow users list from config.ini
  const authorizedUsers = config.authorized_users.users
    ? config.authorized_users.users.split(',').map(user => user.trim())
    : [];

  // we've got a connection to XMPP!
  xmpp.on('online', async () => {
    console.log('Connected with JID: ' + config.xmpp.user);
    console.log('Pubkey: ' + pk);
    await xmpp.send(xml('presence', {}, xml('status', {}, 'https://nostrsms.com')));
  });

  // Debug: Log all incoming stanzas
  xmpp.on('stanza', async (stanza) => {
    console.log('Received stanza:', stanza.toString());

    // check for chat messages
    if (stanza.is('message') && stanza.attrs.type === 'chat') {
      // store from jid, and message contents
      const from = stanza.attrs.from;
      const body = stanza.getChildText('body');

      // show them to the user in console
      console.log(`Message from ${from}: ${body}`);

      // if there is no body, it's not a message.
      // it just qqs
      if (!body) return;

      // deny messages if they aren't a valid user
      if (!authorizedUsers.includes(from)) {
        await xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, 'Not authorized')));
        return;
      }
      // user needs help
      // let's send them a wiki link back. (This should be nostrfied....right?!)
      if (body === '!help') {
        await xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, 'https://wiki.vanderwarker.family/doku.php?id=code:nostrsms:commands')));
      // fetch global posts
      } else if (body === '!g') {
        await xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, `Here are the latest 10 posts from global on ${config.relays.read}`)));

        async function getGlobalEvents() {
          // store read relays from config.ini
          const relay = nostr.relayInit(config.relays.read, WebSocket);
          // we've got a connection to  the nostr!
          relay.on('connect', () => console.log(`connected to ${relay.url}`));
          // we've not got a connection to the nostr!
          relay.on('error', () => console.log(`failed to connect to ${relay.url}`));

          // attempt connection to the nostr
          await relay.connect();

          // create a subscription to get events
          let sub = relay.sub([{ kinds: [1], limit: 10 }]);
          // we've got an event!
          sub.on('event', event => {
            // check if the event recieved is valid json
            // if it is parse it
            const globalEvents = typeof event === 'string' ? JSON.parse(event) : event;
            // check for json data
            // continue if it exists (atomic)
            if (globalEvents && globalEvents.content && globalEvents.pubkey && globalEvents.created_at) {
              // store parsed data from event
              const content = globalEvents.content;
              const author = globalEvents.pubkey;
              const createdAt = globalEvents.created_at;
              // print out to console
              console.log('Content:', content);
              console.log('Author:', author);
              console.log('Created At:', createdAt);
              // send parsed event data to XMPP
              xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, `"${content}", - ${author} @ ${createdAt} `)));
              // All set! Let's disconnect from the relay...or...
              // I am not sure if I will change this...
              relay.close();
            } else {
              // event recieved wasn't a valid nostr event
              console.error('Invalid event structure or missing required properties.');
            }
          });
        }
        // run and print errors to console
        getGlobalEvents().catch(console.error);
      } else {
        // user hasn't input a command, which defaults to a nostr post.
        async function newPost() {
          // store write relays from config.ini
          const relay = nostr.relayInit(config.relays.write, WebSocket);
          // we've got a connection to  the nostr!
          relay.on('connect', () => console.log(`connected to ${relay.url}`));
          // we've not got a connection to the nostr!
          relay.on('error', () => console.log(`failed to connect to ${relay.url}`));
          
          // attempt connection to the nostr
          await relay.connect();
          /*
          incredible, but this is a nostr event.
          truth be told it doesn't even need the tags. 
          That's just referencing another type of
          event that advertises nostrsms and shows 
          links to this source code. nostr is 
          waaayyy more than a X (Twitter) replacement
          */            
          
          let event = {
            // kind 1 is a basic text post
            kind: 1,
            // set users pubkey
            pubkey: pk,
            // this will be created using a Unix timestamp
            created_at: Math.floor(Date.now() / 1000),
            // some clients will show via NostrSMS much like X has/had? idk haven't been on in years :P
            tags: [['client', 'NostrSMS', '31990:9be1b8315248eeb20f9d9ab2717d1750e4f27489eab1fa531d679dadd34c2f8d:1726937516']],
            // set event content to the message recieved from the user.
            content: body,
          };
          // sign the nostr event using privkey from config.ini
          const signedEvent = nostr.finishEvent(event, sk);
          // store event id
          letsCID = signedEvent.id;
          // sends id and pubkey to function below
          showSID(letsCID, pk);
          // 
          await relay.publish(signedEvent);
          relay.close();
        }
        // run and print errors to console
        newPost().catch(console.error);

        // hey look the function below!
        
        // gets id and pk from above function ;)
        async function showSID(letsCID, pk) {
          // print id to console
          console.log(letsCID);
          // print pubkey to console
          console.log(pk);
          // takes the event id, your write relay(s), pubkey, and kind 1
          // mashing it together into a nevent
          // (makes it easier for other clients to find the event)
          const event = {
            id: letsCID,
            relays: [config.relays.write],
            author: pk,
            kind: 1,
          };

          // encode event using nip-19 (nevent)
          const encodedNEvent = nostr.nip19.neventEncode(event);
          // send the nevent back to the user
          // the can copy and paste this into most nostr
          // clients and get the event
          await xmpp.send(xml('message', { to: from, type: 'chat' }, xml('body', {}, 'nostr:' + encodedNEvent)));
          // also print nevent to console
          console.log(encodedNEvent);
        }
      }
    }
  });
  // uh-oh, XMPP error
  xmpp.on('error', (err) => {
    // print error to console
    console.error('XMPP Error:', err);
  });
  // start XMPP connectioh
  async function startXMPP() {
    try {
      // wait for connection
      await xmpp.start();
      // print success message to console
      console.log('🚀 XMPP Started Successfully');
    } catch (err) {
      // print success message to console
      console.error('❌ Failed to Start XMPP:', err);
      // kill nostrsms process to let user try again
      process.exit(1);
    }
  }

  // attempt to connect to XMPP
  startXMPP();

  // user hit ctrl+c! 
  process.on('SIGINT', function () {
    console.log("SigInt received. Shutdown inevitable!");
    xmpp.stop();
    process.exit();
  });


// config.ini is invalid, or missing.
// print wiki link to console for further instructions
} else {
  console.log("=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=");
  console.error(`Config file not found at ${configPath}`);
  console.log("=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=_-_+_-_=");
  console.log("");
  console.log("Please create a config.ini");
  console.log("See https://wiki.vanderwarker.family/doku.php?id=code:nostrsms:install");
  console.log("and choose your preferred install method.");
  process.exit(1);
}