(function($){
  'use strict';

  function getSearchParams(url) {
    try {
      return new URL(url).searchParams;
    } catch (e) {
      return null;
    }
  }

  $(function(){
    $(document).on('submit', '#calliope_sp_publish_form', function(){
      var $form = $(this);
      if ($form.data('calliopeSpSubmitting')) {
        return false;
      }

      $form.data('calliopeSpSubmitting', true);
      var $button = $form.find('button[type="submit"]').first();
      if ($button.length) {
        var publishingLabel = $button.attr('data-publishing-label') || 'Publishing…';
        $button.prop('disabled', true).text(publishingLabel);
      }
      return true;
    });

    try {
      var params = new URLSearchParams(window.location.search || '');
      if (params.get('page') !== 'calliope-social-publisher') return;

      var tab = params.get('tab') || 'editor';
      if (tab !== 'connections') return;

      // Store selection when clicking network tabs.
      $(document).on('click', '.calliope-sp-net-tab', function(){
        var sp = getSearchParams(this.href);
        if (!sp) return;
        var net = sp.get('net');
        if (net) {
          try { window.localStorage.setItem('calliope_sp_last_net', net); } catch (e) {}
        }
      });

      var currentNet = params.get('net');
      if (currentNet) {
        try { window.localStorage.setItem('calliope_sp_last_net', currentNet); } catch (e) {}
        return;
      }

      // If we landed without ?net=..., restore the last one (helps after POST redirects).
      var lastNet = null;
      try { lastNet = window.localStorage.getItem('calliope_sp_last_net'); } catch (e) {}

      if (lastNet) {
        params.set('net', lastNet);
        var newUrl = window.location.pathname + '?' + params.toString() + (window.location.hash || '');
        window.location.replace(newUrl);
      }
    } catch (e) {
      // noop
    }
  });
})(jQuery);

(function($){
  'use strict';

  $(function(){
    var cfg = window.CALLIOPE_SP_JOB_RUNNER || {};
    var jobs = Array.isArray(cfg.jobs) ? cfg.jobs : [];
    if (!jobs.length || !cfg.ajaxUrl || !cfg.nonce) {
      return;
    }

    var i18n = cfg.i18n || {};
    var networkLabels = cfg.networkLabels || {};
    var params;

    try {
      params = new URLSearchParams(window.location.search || '');
    } catch (e) {
      params = null;
    }

    var jobStates = {};
    jobs.forEach(function(job){
      if (!job || !job.job_id) {
        return;
      }

      var destinations = Array.isArray(job.destinations) ? job.destinations.slice() : [];
      var completed = Array.isArray(job.completed) ? job.completed.slice() : [];
      jobStates[job.job_id] = {
        destinations: destinations,
        completed: completed,
        current: '',
        done: false,
        failed: false,
        failedNetworks: []
      };
    });

    function networkLabel(network) {
      network = String(network || '');
      return networkLabels[network] || network.charAt(0).toUpperCase() + network.slice(1);
    }

    function format(template) {
      var args = Array.prototype.slice.call(arguments, 1);
      template = String(template || '');
      args.forEach(function(arg, index){
        var numbered = new RegExp('%' + (index + 1) + '\\$[sd]', 'g');
        template = template.replace(numbered, String(arg));
        template = template.replace(/%[sd]/, String(arg));
      });
      return template;
    }

    function uniqueValues(values) {
      var seen = {};
      var output = [];
      values.forEach(function(value){
        value = String(value || '');
        if (!value || seen[value]) {
          return;
        }
        seen[value] = true;
        output.push(value);
      });
      return output;
    }

    function getTotals() {
      var total = 0;
      var completed = 0;

      Object.keys(jobStates).forEach(function(jobId){
        var state = jobStates[jobId];
        var destinations = Array.isArray(state.destinations) ? state.destinations : [];
        var done = uniqueValues(Array.isArray(state.completed) ? state.completed : []);
        total += destinations.length;
        completed += done.filter(function(network){ return destinations.indexOf(network) !== -1; }).length;
      });

      if (total < 1) {
        total = 1;
      }

      completed = Math.max(0, Math.min(completed, total));
      return {
        total: total,
        completed: completed,
        percent: Math.round((completed / total) * 100)
      };
    }

    function nextPendingNetwork(job) {
      var state = jobStates[job.job_id] || {};
      var destinations = Array.isArray(state.destinations) ? state.destinations : [];
      var completed = Array.isArray(state.completed) ? state.completed : [];
      for (var i = 0; i < destinations.length; i++) {
        if (completed.indexOf(destinations[i]) === -1) {
          return destinations[i];
        }
      }
      return '';
    }

    function markCompleted(job, network, ok) {
      if (!job || !job.job_id || !network || !jobStates[job.job_id]) {
        return;
      }
      var state = jobStates[job.job_id];
      if (state.completed.indexOf(network) === -1) {
        state.completed.push(network);
      }
      if (ok === false) {
        state.failed = true;
        state.failedNetworks = Array.isArray(state.failedNetworks) ? state.failedNetworks : [];
        if (state.failedNetworks.indexOf(network) === -1) {
          state.failedNetworks.push(network);
        }
      }
      state.current = '';
    }

    function markFromReport(job, report) {
      if (!job || !job.job_id || !jobStates[job.job_id] || !report || !report.results) {
        return false;
      }
      Object.keys(report.results).forEach(function(network){
        var result = report.results[network] || {};
        markCompleted(job, network, result.ok !== false);
      });
      return true;
    }

    function hasAnyFailures() {
      return Object.keys(jobStates).some(function(jobId){
        var state = jobStates[jobId] || {};
        return !!(state.failed || (Array.isArray(state.failedNetworks) && state.failedNetworks.length));
      });
    }

    function markJobDone(job) {
      if (!job || !job.job_id || !jobStates[job.job_id]) {
        return;
      }
      var state = jobStates[job.job_id];
      state.completed = uniqueValues((state.completed || []).concat(state.destinations || []));
      state.current = '';
      state.done = true;
    }

    function makeNotice() {
      var $wrap = $('.wrap').first();
      if (!$wrap.length) {
        return $();
      }

      var $notice = $('#calliope-sp-job-runner-notice');
      if ($notice.length) {
        return $notice;
      }

      $notice = $('<div/>', {
        id: 'calliope-sp-job-runner-notice',
        class: 'notice notice-info calliope-sp-job-runner-notice'
      });

      var $panel = $('<div/>', { class: 'calliope-sp-job-progress' });
      var $header = $('<div/>', { class: 'calliope-sp-job-progress__header' });
      $header.append($('<div/>', { class: 'calliope-sp-job-progress__title' }).text(i18n.progressTitle || 'Publication in progress'));
      $header.append($('<div/>', { class: 'calliope-sp-job-progress__percent' }).text('0%'));

      $panel.append($header);
      $panel.append($('<div/>', { class: 'calliope-sp-job-progress__status' }).text(i18n.starting || 'Starting background publication…'));
      $panel.append(
        $('<div/>', { class: 'calliope-sp-job-progress__bar', role: 'progressbar', 'aria-valuemin': '0', 'aria-valuemax': '100', 'aria-valuenow': '0' })
          .append($('<span/>', { class: 'calliope-sp-job-progress__bar-fill' }))
      );
      $panel.append($('<div/>', { class: 'calliope-sp-job-progress__meta' }));
      $panel.append($('<div/>', { class: 'calliope-sp-job-progress__networks' }));
      $panel.append($('<div/>', { class: 'calliope-sp-job-progress__help' }).text(i18n.progressHelp || 'Keep this tab open until the process finishes.'));

      $notice.append($panel);
      $wrap.prepend($notice);
      return $notice;
    }

    function renderNetworkChips($notice) {
      var $networks = $notice.find('.calliope-sp-job-progress__networks');
      if (!$networks.length) {
        return;
      }

      $networks.empty();
      Object.keys(jobStates).forEach(function(jobId){
        var state = jobStates[jobId];
        var destinations = Array.isArray(state.destinations) ? state.destinations : [];
        var completed = Array.isArray(state.completed) ? state.completed : [];
        destinations.forEach(function(network){
          var statusClass = 'is-pending';
          var statusText = i18n.waiting || 'Waiting…';
          if (completed.indexOf(network) !== -1) {
            if (Array.isArray(state.failedNetworks) && state.failedNetworks.indexOf(network) !== -1) {
              statusClass = 'is-error';
              statusText = '!';
            } else {
              statusClass = 'is-complete';
              statusText = '✓';
            }
          } else if (state.current === network) {
            statusClass = 'is-current';
            statusText = '…';
          } else if (state.failed) {
            statusClass = 'is-pending';
          }

          $networks.append(
            $('<span/>', { class: 'calliope-sp-job-progress__chip ' + statusClass })
              .append($('<strong/>').text(networkLabel(network)))
              .append($('<em/>').text(statusText))
          );
        });
      });
    }

    function updateNotice(text, type) {
      var $notice = makeNotice();
      if (!$notice.length) {
        return;
      }

      var totals = getTotals();
      var percent = totals.percent;
      $notice.removeClass('notice-info notice-success notice-error notice-warning');
      $notice.addClass(type || 'notice-info');
      $notice.find('.calliope-sp-job-progress__status').text(text || '');
      $notice.find('.calliope-sp-job-progress__percent').text(percent + '%');
      $notice.find('.calliope-sp-job-progress__bar').attr('aria-valuenow', String(percent));
      $notice.find('.calliope-sp-job-progress__bar-fill').css('width', percent + '%');
      $notice.find('.calliope-sp-job-progress__meta').text(format(i18n.progressCount || '%1$d of %2$d networks processed', totals.completed, totals.total));
      renderNetworkChips($notice);
    }

    function refreshLogsOnce() {
      if (!params || params.get('page') !== 'calliope-social-publisher') {
        return;
      }
      if (params.get('calliope_sp_runner_refresh') === '1') {
        return;
      }
      params.set('calliope_sp_runner_refresh', '1');
      window.setTimeout(function(){
        window.location.replace(window.location.pathname + '?' + params.toString() + (window.location.hash || ''));
      }, 1400);
    }

    function hasValidJob(job) {
      return !!(job && job.job_id && job.token && jobStates[job.job_id]);
    }

    function finishAllJobs() {
      if (hasAnyFailures()) {
        updateNotice((i18n.doneWithErrors) || 'Publication finished with some errors. Refreshing logs…', 'notice-warning');
      } else {
        updateNotice((i18n.done) || 'Publication finished. Refreshing logs…', 'notice-success');
      }
      refreshLogsOnce();
    }

    function runStep(job, onDone) {
      if (!hasValidJob(job)) {
        onDone();
        return;
      }

      job._attempts = job._attempts || 0;
      var current = nextPendingNetwork(job);
      if (jobStates[job.job_id]) {
        jobStates[job.job_id].current = current;
      }

      var statusText = current ? format(i18n.runningNetwork || 'Publishing %s…', networkLabel(current)) : (i18n.running || 'Publishing in safe steps…');
      updateNotice(statusText, 'notice-info');

      $.ajax({
        url: cfg.ajaxUrl,
        method: 'POST',
        dataType: 'json',
        timeout: 120000,
        data: {
          action: 'calliope_sp_run_publish_job_step',
          nonce: cfg.nonce,
          job_id: job.job_id,
          token: job.token
        }
      }).done(function(response){
        if (!response || !response.success) {
          job._attempts += 1;
          if (job._attempts < 3) {
            updateNotice((i18n.running || 'Publishing in safe steps…') + ' ' + (job._attempts + 1) + '/3', 'notice-warning');
            window.setTimeout(function(){ runStep(job, onDone); }, 4000);
            return;
          }
          if (jobStates[job.job_id]) {
            jobStates[job.job_id].failed = true;
            jobStates[job.job_id].current = '';
          }
          updateNotice((i18n.failed) || 'The browser worker could not continue. Refresh this page and check Logs.', 'notice-error');
          onDone();
          return;
        }

        job._attempts = 0;
        var data = response.data || {};
        if (data.processed) {
          markCompleted(job, data.processed, data.ok !== false);
          if (data.ok === false) {
            updateNotice(format(i18n.failedNetwork || '%s failed. Continuing with the next network…', networkLabel(data.processed)), 'notice-warning');
          } else {
            updateNotice(format(i18n.completedNetwork || '%s published.', networkLabel(data.processed)), 'notice-info');
          }
        }

        if (data.done) {
          if (!markFromReport(job, data.report || null)) {
            markJobDone(job);
          }
          onDone();
          return;
        }

        window.setTimeout(function(){ runStep(job, onDone); }, data.running ? 3500 : 900);
      }).fail(function(){
        job._attempts += 1;
        if (job._attempts < 3) {
          updateNotice((i18n.running || 'Publishing in safe steps…') + ' ' + (job._attempts + 1) + '/3', 'notice-warning');
          window.setTimeout(function(){ runStep(job, onDone); }, 5000);
          return;
        }
        if (jobStates[job.job_id]) {
          jobStates[job.job_id].failed = true;
          jobStates[job.job_id].current = '';
        }
        updateNotice((i18n.failed) || 'The browser worker could not continue. Refresh this page and check Logs.', 'notice-error');
        onDone();
      });
    }

    var validJobs = jobs.filter(hasValidJob);
    var currentJobIndex = 0;

    function runNextJob() {
      if (currentJobIndex >= validJobs.length) {
        finishAllJobs();
        return;
      }

      var job = validJobs[currentJobIndex];
      runStep(job, function(){
        currentJobIndex += 1;
        runNextJob();
      });
    }

    updateNotice(i18n.starting || 'Starting background publication…', 'notice-info');
    runNextJob();
  });
})(jQuery);
