(function ($) {
    'use strict';

    var cfg = window.EncypherProvenanceClassic || {};
    var POLL_INTERVAL = 3000;
    var PROVENANCE_STATES = ['not_signed', 'signing', 'signed', 'verified', 'edited', 'needs_attention'];
    var ASSURANCE_STATES = ['unknown', 'held', 'live_unverified', 'live_verified', 'failed', 'cancelled', 'superseded'];

    function exactState(value, allowed, fallback) {
        return allowed.indexOf(value) !== -1 ? value : fallback;
    }

    function normalize(response) {
        response = response && typeof response === 'object' ? response : {};
        var assurance = response.assurance && typeof response.assurance === 'object' ? response.assurance : {};
        var facts = assurance.publish_facts && typeof assurance.publish_facts === 'object'
            ? assurance.publish_facts
            : (response.publish_facts && typeof response.publish_facts === 'object' ? response.publish_facts : {});
        return {
            provenance_state: exactState(response.provenance_state, PROVENANCE_STATES, 'not_signed'),
            assurance_state: exactState(response.assurance_state, ASSURANCE_STATES, 'unknown'),
            display_state: exactState(response.display_state, PROVENANCE_STATES, 'not_signed'),
            internal_state: assurance.internal_state || response.internal_state || '',
            severity: assurance.severity || response.severity || 'none',
            failure: response.failure || assurance.failure || response.last_sign_error || null,
            receipt: response.receipt || assurance.receipt || {},
            actions: response.actions || assurance.actions || {},
            publication_generation: response.publication_generation || assurance.publication_generation || '',
            publish_facts: facts
        };
    }

    function phase(state) {
        if (state.severity === 'bypassed' || state.internal_state === 'emergency_unsigned') return 'blocked';
        if (state.publish_facts.fail_open === true) return 'fail_open';
        if (state.assurance_state === 'held') return 'held';
        if (state.assurance_state === 'live_verified') return 'signed_live';
        if (state.assurance_state === 'failed' || state.display_state === 'needs_attention') return 'blocked';
        return 'will_sign';
    }

    function label(state) {
        if (state.severity === 'bypassed' || state.internal_state === 'emergency_unsigned') return 'Emergency unsigned publication';
        if (phase(state) === 'fail_open' && state.display_state === 'needs_attention') return 'Signing failed; post is live';
        if (phase(state) === 'fail_open') return 'Fail-open publishing';
        if (state.assurance_state === 'held') return 'Held for signing';
        if (state.assurance_state === 'live_verified') return 'Signed and live';
        if (state.assurance_state === 'cancelled') return 'Held publication cancelled';
        if (state.assurance_state === 'superseded') return 'Superseded';
        if (state.assurance_state === 'failed') return 'Protection failed';
        var labels = {
            not_signed: 'Not signed',
            signing: 'Queued',
            signed: 'Signed',
            verified: 'Verified',
            edited: 'Edited since signing',
            needs_attention: 'Signing failed'
        };
        return labels[state.display_state] || labels.not_signed;
    }

    function shouldPoll(state) {
        return state.provenance_state === 'signing'
            || state.display_state === 'signing'
            || state.assurance_state === 'held'
            || state.assurance_state === 'live_unverified';
    }

    function route(path) {
        return cfg.restUrl + path;
    }


    function setStatus($container, state, message, cls) {
        $container
            .attr('data-encypher-provenance-state', state.provenance_state)
            .attr('data-encypher-assurance-state', state.assurance_state)
            .attr('data-encypher-display-state', state.display_state)
            .attr('data-encypher-publish-phase', phase(state))
            .attr('data-generation', state.publication_generation || '');
        $container.find('.status-value')
            .text(label(state))
            .attr('data-status', state.display_state)
            .attr('data-encypher-provenance-state', state.provenance_state)
            .attr('data-encypher-assurance-state', state.assurance_state)
            .attr('data-encypher-display-state', state.display_state);
        var $msg = $container.find('.status-message');
        $msg.text(message || '').removeClass('error success');
        if (message && cls) $msg.addClass(cls);
        if (state.receipt && state.receipt.url) {
            $container.find('.verification-link').show().find('a').attr('href', state.receipt.url);
        }
        $container.find('.encypher-provenance-retry')
            .text(state.display_state === 'not_signed' ? 'Sign now' : 'Retry failed signing')
            .prop('disabled', state.actions.can_retry === false);
        var $cancel = $container.find('.encypher-provenance-cancel');
        var $actions = $container.find('.encypher-classic-actions');
        var protectedCandidate = state.publish_facts.protected_publish_enabled !== false
            && state.publish_facts.fail_open !== true;
        var canCancel = protectedCandidate
            && (state.assurance_state === 'held' || state.assurance_state === 'failed')
            && state.actions.can_cancel === true
            && state.publication_generation;
        if (canCancel && typeof $actions.append === 'function' && !$cancel.length) {
            $actions.append('<button type="button" class="button-link-delete encypher-provenance-cancel">Cancel held publication</button>');
            $cancel = $container.find('.encypher-provenance-cancel');
        }
        if (canCancel && typeof $cancel.show === 'function') {
            $cancel.attr('data-generation', state.publication_generation).show().prop('disabled', false);
        } else if (typeof $cancel.hide === 'function') {
            $cancel.hide();
        }
    }

    function requestStatus($container, postId, announce) {
        var postType = $container.data('post-type') || '';
        var endpoint = postType === 'attachment' ? 'media-status' : 'status';
        var query = postType === 'attachment' ? 'attachment_id=' : 'post_id=';
        var join = cfg.restUrl.indexOf('?') === -1 ? '?' : '&';
        return $.ajax({
            url: route(endpoint) + join + query + postId,
            method: 'GET',
            beforeSend: function (xhr) {
                xhr.setRequestHeader('X-WP-Nonce', cfg.nonce);
            }
        }).done(function (response) {
            var state = normalize(response);
            setStatus($container, state, '', '');
            var reportUrl = response.verification_url || response.manifest_url || (state.receipt && state.receipt.url) || '';
            if (reportUrl) $container.find('.verification-link').show().find('a').attr('href', reportUrl);
            if (shouldPoll(state)) {
                setTimeout(function () { requestStatus($container, postId, false); }, POLL_INTERVAL);
            } else if (announce) {
                var failed = state.assurance_state === 'failed' || state.display_state === 'needs_attention';
                var message = failed && state.failure && state.failure.message
                    ? state.failure.message
                    : label(state);
                setStatus($container, state, message, failed ? 'error' : 'success');
            }
        }).fail(function () {
            var failed = normalize({ provenance_state: 'needs_attention', assurance_state: 'failed', display_state: 'needs_attention' });
            setStatus($container, failed, 'Could not refresh Encypher status. Try again.', 'error');
        });
    }

    $(document).on('click', '.encypher-provenance-verify', function (event) {
        event.preventDefault();
        var $button = $(this);
        var $container = $button.closest('.encypher-provenance-classic');
        var postId = $button.data('post-id') || $container.data('post-id');
        $button.prop('disabled', true);
        requestStatus($container, postId, true).always(function () {
            $button.prop('disabled', false);
        });
    });

    $(document).on('click', '.encypher-provenance-retry', function (event) {
        if (event && typeof event.preventDefault === 'function') event.preventDefault();
        var $button = $(this);
        var $container = $button.closest('.encypher-provenance-classic');
        var postId = $button.data('post-id') || $container.data('post-id');
        var queued = normalize({ provenance_state: 'signing', assurance_state: 'held', display_state: 'signing' });
        $button.prop('disabled', true);
        setStatus($container, queued, 'Queued for Encypher signing.', '');
        $.ajax({
            url: route('retry'),
            method: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({ post_id: postId }),
            beforeSend: function (xhr) {
                xhr.setRequestHeader('X-WP-Nonce', cfg.nonce);
            }
        }).done(function () {
            setTimeout(function () { requestStatus($container, postId, true); }, POLL_INTERVAL);
        }).fail(function (xhr) {
            var message = xhr && xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : 'Retry failed.';
            var failed = normalize({ provenance_state: 'needs_attention', assurance_state: 'failed', display_state: 'needs_attention', failure: { message: message } });
            setStatus($container, failed, message, 'error');
        }).always(function () {
            $button.prop('disabled', false);
        });
    });

    $(document).on('click', '.encypher-provenance-cancel', function (event) {
        event.preventDefault();
        var $button = $(this);
        var $container = $button.closest('.encypher-provenance-classic');
        var postId = $button.data('post-id') || $container.data('post-id');
        var generation = $button.data('generation')
            || $button.attr('data-generation')
            || $container.data('generation')
            || $container.attr('data-generation');
        if (!generation || (typeof window.confirm === 'function' && !window.confirm('Cancel this held publication? The candidate will not be published.'))) return;
        $button.prop('disabled', true);
        $.ajax({
            url: route('publication/cancel'),
            method: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({ post_id: postId, generation: generation }),
            beforeSend: function (xhr) { xhr.setRequestHeader('X-WP-Nonce', cfg.nonce); }
        }).done(function () {
            requestStatus($container, postId, true);
        }).fail(function (xhr) {
            var message = xhr && xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : 'Held publication could not be cancelled.';
            var failed = normalize({ provenance_state: 'needs_attention', assurance_state: 'failed', display_state: 'needs_attention', failure: { message: message } });
            setStatus($container, failed, message, 'error');
        }).always(function () {
            $button.prop('disabled', false);
        });
    });

    $(function () {
        $('.encypher-provenance-classic').each(function () {
            var $container = $(this);
            var postId = $container.data('post-id');
            if (postId) requestStatus($container, postId, false);
        });
    });

    window.EncypherClassicUX = {
        normalize: normalize,
        phase: phase,
        label: label,
        shouldPoll: shouldPoll
    };
}(jQuery));
