<?php

declare(strict_types=1);

namespace FecaMailshots\Admin;

use FecaMailshots\Application\DataSourceService;
use FecaMailshots\WordPress\WordPressFacade;

final class ReviewRecipientsAdminPage
{
    use AdminRequestHelpers;

    private const CAPABILITY = 'edit_pages';
    private const PAGE_SLUG = 'feca-mailshots-review-recipients';

    /** @var callable(): DataSourceService */
    private $serviceFactory;
    private WordPressFacade $wp;

    /** @param callable(): DataSourceService $serviceFactory */
    public function __construct(callable $serviceFactory, WordPressFacade $wp)
    {
        $this->serviceFactory = $serviceFactory;
        $this->wp = $wp;
    }

    public function register(): void
    {
        $this->wp->addAction('admin_menu', [$this, 'registerMenu']);
        $this->wp->addAction('admin_post_feca_mailshots_review_recipients_api', [$this, 'handleApi']);
    }

    public function registerMenu(): void
    {
        $this->wp->addSubmenuPage('feca-mailshot', 'Review Recipients', 'Review Recipients', self::CAPABILITY, self::PAGE_SLUG, [$this, 'render']);
    }

    public function render(): void
    {
        if (!$this->wp->currentUserCan(self::CAPABILITY)) {
            echo 'Permission denied';
            return;
        }

        $service = $this->service();
        $sources = $service->list();
        $selectedSource = trim((string) ($this->wp->requestParam('data_source', '') ?? ''));

        $initialRows = [];
        $initialColumns = [];
        $initialCount = 0;
        $initialErrors = [];
        if ($selectedSource !== '') {
            $dsl = $this->dslForSource($sources, $selectedSource);
            if ($dsl === '') {
                $initialErrors[] = 'Selected data source was not found.';
            } else {
                $result = $service->review($dsl);
                $initialErrors = array_values(array_map('strval', (array) ($result['errors'] ?? [])));
                if ($initialErrors === []) {
                    $initialRows = is_array($result['rows'] ?? null) ? $result['rows'] : [];
                    $initialColumns = is_array($result['columns'] ?? null) ? array_values(array_map('strval', $result['columns'])) : [];
                    $initialCount = (int) ($result['count'] ?? count($initialRows));
                }
            }
        }

        echo '<div class="wrap feca-mailshots-admin"><h1>Review Recipients</h1>';
        echo $this->renderAdminUiStyles();
        echo '<p>Inspect recipient rows for a selected data source with full-field filtering and sorting.</p>';

        echo '<div class="feca-panel">';
        echo '<div class="feca-control-row">';
        echo '<div class="feca-control feca-control-min-320">';
        echo '<label for="rr_data_source"><strong>Data Source</strong></label>';
        echo '<select id="rr_data_source" class="feca-minw-220">';
        echo '<option value="">Select data source</option>';
        foreach ($sources as $source) {
            $name = trim((string) ($source['name'] ?? ''));
            if ($name === '') {
                continue;
            }
            $selected = $name === $selectedSource ? ' selected' : '';
            echo '<option value="' . htmlspecialchars($name, ENT_QUOTES) . '"' . $selected . '>' . htmlspecialchars($name) . '</option>';
        }
        echo '</select></div>';
        echo '<div class="feca-control feca-control-min-360">';
        echo '<label for="rr_filter"><strong>Filter by</strong></label>';
        echo '<input id="rr_filter" class="regular-text feca-minw-240" type="text" placeholder="Matches any field">';
        echo '</div>';
        echo '<div class="feca-control feca-control-min-260">';
        echo '<label for="rr_sort_by"><strong>Sort by</strong></label>';
        echo '<select id="rr_sort_by" class="feca-minw-170"><option value="">(no fields)</option></select>';
        echo '</div>';
        echo '<div class="feca-control feca-control-min-280">';
        echo '<label for="rr_sort_direction"><strong>Sort Direction</strong></label>';
        echo '<select id="rr_sort_direction" class="feca-minw-140"><option value="asc">Ascending</option><option value="desc">Descending</option></select>';
        echo '</div>';
        echo '</div></div>';

        echo '<div id="rr_status" class="feca-banner">';
        echo 'Select a data source to load recipients.';
        echo '</div>';
        echo '<div id="rr_error" class="feca-banner feca-banner-error feca-hidden"></div>';
        echo '<div class="feca-scroll-frame">';
        echo '<div id="rr_scroll" class="feca-scroll-pane">';
        echo '<table class="widefat striped feca-table-wide" id="rr_table">';
        echo '<colgroup id="rr_cols"></colgroup>';
        echo '<thead><tr id="rr_head_row"><th>No recipients loaded.</th></tr></thead>';
        echo '<tbody id="rr_body"></tbody></table>';
        echo '</div>';
        echo '</div>';

        $sourceDslMap = [];
        foreach ($sources as $source) {
            $name = trim((string) ($source['name'] ?? ''));
            if ($name === '') {
                continue;
            }
            $sourceDslMap[$name] = trim((string) ($source['dsl_text'] ?? ''));
        }
        echo '<script>';
        echo 'window.fecaReviewRecipientsConfig = ' . json_encode([
            'api' => $this->wp->adminUrl('admin-post.php?action=feca_mailshots_review_recipients_api'),
            'pageSlug' => self::PAGE_SLUG,
            'selectedSource' => $selectedSource,
            'sources' => array_values(array_keys($sourceDslMap)),
            'sourceDslMap' => $sourceDslMap,
            'initial' => [
                'ok' => $initialErrors === [],
                'errors' => $initialErrors,
                'rows' => $initialRows,
                'columns' => $initialColumns,
                'count' => $initialCount,
                'source' => $selectedSource,
            ],
        ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ';';
        echo '(function(){';
        echo 'var cfg=window.fecaReviewRecipientsConfig||{};';
        echo 'var sourceSel=document.getElementById("rr_data_source");';
        echo 'var filterInput=document.getElementById("rr_filter");';
        echo 'var sortSel=document.getElementById("rr_sort_by");';
        echo 'var dirSel=document.getElementById("rr_sort_direction");';
        echo 'var statusEl=document.getElementById("rr_status");';
        echo 'var errEl=document.getElementById("rr_error");';
        echo 'var scrollWrap=document.getElementById("rr_scroll");';
        echo 'var colsEl=document.getElementById("rr_cols");';
        echo 'var headRow=document.getElementById("rr_head_row");';
        echo 'var bodyEl=document.getElementById("rr_body");';
        echo 'var cache={};';
        echo 'var storagePrefix="fecaReviewRecipientsSelection::";';
        echo 'var state={source:"",filter:"",sortBy:"",sortDirection:"asc",selectedKeys:{}};';
        echo 'function text(v){if(v===null||v===undefined){return "";}if(typeof v==="string"){return v;}if(typeof v==="number"||typeof v==="boolean"){return String(v);}try{return JSON.stringify(v);}catch(_){return String(v);}}';
        echo 'function clearErr(){errEl.classList.add("feca-hidden");errEl.textContent="";}';
        echo 'function showErr(msg){errEl.classList.remove("feca-hidden");errEl.textContent=msg||"Unknown error";}';
        echo 'function status(msg){statusEl.textContent=msg||"";}';
        echo 'function setColumnWidths(cols){var widthPx=170;var c="<col style=\\"width:44px;min-width:44px;max-width:44px\\">";for(var i=0;i<cols.length;i++){c+="<col style=\\"width:"+widthPx+"px;min-width:"+widthPx+"px;max-width:"+widthPx+"px\\">";}colsEl.innerHTML=c;}';
        echo 'function rebuildSortColumns(cols,keep){sortSel.innerHTML="";if(!cols||!cols.length){var o=document.createElement("option");o.value="";o.textContent="(no fields)";sortSel.appendChild(o);state.sortBy="";return;}cols.forEach(function(c,idx){var o=document.createElement("option");o.value=c;o.textContent=c;if((keep&&c===keep)||(!keep&&idx===0)){o.selected=true;state.sortBy=c;}sortSel.appendChild(o);});}';
        echo 'function isIdField(name){var n=String(name||"").trim().toLowerCase();if(!n){return false;}return n==="id"||n.endsWith(".id")||n.endsWith("_id")||n==="accountid"||n.endsWith(".accountid");}';
        echo 'function normalizeColumns(rawCols,rows){var cols=(rawCols||[]).slice();if(cols.length===0&&rows&&rows.length){var seen={};rows.forEach(function(r){Object.keys(r||{}).forEach(function(k){if(!seen[k]){seen[k]=1;cols.push(k);}});});}var seenOut={};var out=[];cols.forEach(function(c){var key=String(c||"").trim();if(!key||isIdField(key)){return;}var lk=key.toLowerCase();if(seenOut[lk]){return;}seenOut[lk]=1;out.push(key);});out.sort(function(a,b){return a.localeCompare(b,undefined,{sensitivity:"base"});});return out;}';
        echo 'function dedupeEquivalentColumns(cols,rows){if(!cols||cols.length<2||!rows||rows.length===0){return cols||[];}var keep=[];var signatures={};var limit=Math.min(rows.length,50);for(var i=0;i<cols.length;i++){var col=cols[i];var sig=[];for(var r=0;r<limit;r++){var row=rows[r]||{};sig.push(text(row[col]));}var sigKey=sig.join("\\u241f").toLowerCase();if(sigKey!==""&&signatures[sigKey]){continue;}signatures[sigKey]=col;keep.push(col);}return keep;}';
        echo 'function rowMatches(row,needle,cols){if(!needle){return true;}var n=needle.toLowerCase();for(var i=0;i<cols.length;i++){var k=cols[i];if(text((row||{})[k]).toLowerCase().indexOf(n)!==-1){return true;}}return false;}';
        echo 'function headerParts(name){var n=String(name||"");var idx=n.lastIndexOf(".");if(idx<=0||idx>=n.length-1){return {prefix:"",field:n};}return {prefix:n.substring(0,idx),field:n.substring(idx+1)};}';
        echo 'function sourceStorageKey(src){return storagePrefix+String(src||"");}';
        echo 'function loadSelection(src){if(!src){return {};}try{var raw=sessionStorage.getItem(sourceStorageKey(src));if(!raw){return {};}var parsed=JSON.parse(raw);if(parsed&&typeof parsed==="object"){return parsed;}}catch(_){ }return {};}';
        echo 'function saveSelection(){if(!state.source){return;}try{sessionStorage.setItem(sourceStorageKey(state.source),JSON.stringify(state.selectedKeys||{}));}catch(_){ }}';
        echo 'function detectKey(row){var r=row||{};if(r.__rr_index!==undefined){return "row_index:"+String(r.__rr_index);}return "";}';
        echo 'function renderTable(){clearErr();var src=state.source;if(!src){setColumnWidths([]);headRow.innerHTML="<th>No recipients loaded.</th>";bodyEl.innerHTML="";status("Select a data source to load recipients.");rebuildSortColumns([], "");return;}var payload=cache[src];if(!payload){setColumnWidths([]);headRow.innerHTML="<th>No recipients loaded.</th>";bodyEl.innerHTML="";status("Loading recipients...");return;}if(payload.errors&&payload.errors.length){setColumnWidths([]);showErr(payload.errors.join("; "));headRow.innerHTML="<th>Unable to render recipients.</th>";bodyEl.innerHTML="";status("Load failed.");rebuildSortColumns([], "");return;}var rows=(payload.rows||[]).map(function(r,i){var out=(r&&typeof r==="object")?Object.assign({},r):{};out.__rr_index=i;return out;});var cols=normalizeColumns(payload.columns||[],rows);cols=dedupeEquivalentColumns(cols,rows);if(cols.length===0){setColumnWidths([]);headRow.innerHTML="<th>No fields</th>";bodyEl.innerHTML="";status("Rows: 0 | Total from query: "+(payload.count||0)+" | Selected: 0");rebuildSortColumns([], "");return;}if(state.sortBy&&cols.indexOf(state.sortBy)===-1){state.sortBy="";}rebuildSortColumns(cols,state.sortBy);var filtered=rows.filter(function(r){return rowMatches(r||{},state.filter,cols);});if(state.sortBy){filtered.sort(function(a,b){var l=text((a||{})[state.sortBy]).toLowerCase();var r=text((b||{})[state.sortBy]).toLowerCase();var cmp=l<r?-1:(l>r?1:0);return state.sortDirection==="desc"?-cmp:cmp;});}';
        echo 'setColumnWidths(cols);headRow.innerHTML="";var selHead=document.createElement("th");selHead.className="feca-rr-head-cell";selHead.innerHTML="<input id=\\"rr_select_all\\" type=\\"checkbox\\" title=\\"Select/Deselect all visible rows\\">";headRow.appendChild(selHead);cols.forEach(function(c){var th=document.createElement("th");th.className="feca-rr-head-cell";var p=headerParts(c);if(p.prefix){th.innerHTML="<div class=\\"feca-rr-head-wrap\\"><span class=\\"feca-rr-head-prefix\\">"+p.prefix+"</span><span class=\\"feca-rr-head-field\\">"+p.field+"</span></div>";}else{th.innerHTML="<div class=\\"feca-rr-head-wrap\\"><span class=\\"feca-rr-head-field\\">"+p.field+"</span></div>";}headRow.appendChild(th);});bodyEl.innerHTML="";filtered.forEach(function(r){var tr=document.createElement("tr");var rowKey=detectKey(r);tr.setAttribute("data-row-key",rowKey);var selTd=document.createElement("td");selTd.className="feca-rr-data-cell";var checked=!!(rowKey&&state.selectedKeys[rowKey]);selTd.innerHTML="<input class=\\"rr-row-select\\" type=\\"checkbox\\" data-key=\\""+String(rowKey).replace(/"/g,"&quot;")+"\\""+(checked?" checked":"")+">";tr.appendChild(selTd);cols.forEach(function(c){var td=document.createElement("td");var v=text((r||{})[c]);td.className="feca-rr-data-cell";td.title=v;td.textContent=v;tr.appendChild(td);});bodyEl.appendChild(tr);});';
        echo 'var allKeys=rows.map(detectKey).filter(function(k){return !!k;});var selectedInSource=0;allKeys.forEach(function(k){if(state.selectedKeys[k]){selectedInSource++;}});var visibleKeys=filtered.map(detectKey).filter(function(k){return !!k;});var allVisibleSelected=visibleKeys.length>0&&visibleKeys.every(function(k){return !!state.selectedKeys[k];});var selAll=document.getElementById("rr_select_all");if(selAll){selAll.checked=allVisibleSelected;selAll.indeterminate=visibleKeys.length>0&&!allVisibleSelected&&visibleKeys.some(function(k){return !!state.selectedKeys[k];});}status("Rows: "+filtered.length+" | Total from query: "+(payload.count||0)+" | Selected: "+selectedInSource+(state.filter?(" | Filter: "+state.filter):""));saveSelection();}';
        echo 'function fetchSource(sourceName){if(!sourceName){state.source="";renderTable();return;}if(cache[sourceName]){state.source=sourceName;renderTable();return;}clearErr();status("Loading recipients...");var url=(cfg.api||"")+""+(String(cfg.api||"").indexOf("?")===-1?"?":"&")+"op=load&data_source="+encodeURIComponent(sourceName);fetch(url,{credentials:"same-origin"}).then(function(r){if(!r.ok){throw new Error("API "+r.status);}return r.json();}).then(function(j){if(!j||j.ok===false){var errors=(j&&j.errors)||[(j&&j.error)||"Unknown API error"];cache[sourceName]={errors:errors,rows:[],columns:[],count:0};}else{cache[sourceName]={errors:[],rows:(j.rows||[]),columns:(j.columns||[]),count:(j.count||0)};}state.source=sourceName;renderTable();}).catch(function(e){cache[sourceName]={errors:[(e&&e.message)||"Request failed"],rows:[],columns:[],count:0};state.source=sourceName;renderTable();});}';
        echo 'function updateUrlSource(sourceName){var url=new URL(window.location.href);url.searchParams.set("page",cfg.pageSlug||"feca-mailshots-review-recipients");if(sourceName){url.searchParams.set("data_source",sourceName);}else{url.searchParams.delete("data_source");}window.history.replaceState({}, "", url.toString());}';
        echo 'sourceSel.addEventListener("change",function(){var s=String(sourceSel.value||"").trim();state.source=s;state.selectedKeys=loadSelection(s);updateUrlSource(s);fetchSource(s);});';
        echo 'filterInput.addEventListener("input",function(){state.filter=String(filterInput.value||"").trim();renderTable();});';
        echo 'sortSel.addEventListener("change",function(){state.sortBy=String(sortSel.value||"").trim();renderTable();});';
        echo 'dirSel.addEventListener("change",function(){state.sortDirection=String(dirSel.value||"asc")==="desc"?"desc":"asc";renderTable();});';
        echo 'headRow.addEventListener("change",function(ev){var t=ev.target;if(!t||t.id!=="rr_select_all"){return;}var checked=!!t.checked;var nodes=bodyEl.querySelectorAll("input.rr-row-select[data-key]");nodes.forEach(function(node){var key=String(node.getAttribute("data-key")||"");if(!key){return;}if(checked){state.selectedKeys[key]=true;}else{delete state.selectedKeys[key];}node.checked=checked;});renderTable();});';
        echo 'bodyEl.addEventListener("change",function(ev){var t=ev.target;if(!t||!t.classList||!t.classList.contains("rr-row-select")){return;}var key=String(t.getAttribute("data-key")||"");if(!key){return;}if(t.checked){state.selectedKeys[key]=true;}else{delete state.selectedKeys[key];}renderTable();});';
        echo 'state.sortDirection="asc";dirSel.value="asc";';
        echo 'if(cfg.initial&&cfg.initial.source&&cfg.initial.ok){cache[cfg.initial.source]={errors:[],rows:(cfg.initial.rows||[]),columns:(cfg.initial.columns||[]),count:(cfg.initial.count||0)};}';
        echo 'if(cfg.initial&&cfg.initial.source&&cfg.initial.errors&&cfg.initial.errors.length){cache[cfg.initial.source]={errors:cfg.initial.errors,rows:[],columns:[],count:0};}';
        echo 'var startSource=String((cfg.selectedSource||sourceSel.value||"")).trim();state.source=startSource;';
        echo 'state.selectedKeys=loadSelection(startSource);';
        echo 'if(startSource){fetchSource(startSource);}else{renderTable();}';
        echo '})();';
        echo '</script>';
    }

    public function handleApi(): void
    {
        if (!$this->enforceCapabilityOrJson(self::CAPABILITY)) {
            return;
        }

        $op = $this->requestString('op', '');
        if ($op !== 'load') {
            $this->wp->sendJson(['ok' => false, 'error' => 'Unknown operation'], 400);
            return;
        }

        $sourceName = trim($this->requestString('data_source', ''));
        if ($sourceName === '') {
            $this->wp->sendJson(['ok' => false, 'errors' => ['data_source is required.']], 400);
            return;
        }

        $sources = $this->service()->list();
        $dsl = $this->dslForSource($sources, $sourceName);
        if ($dsl === '') {
            $this->wp->sendJson(['ok' => false, 'errors' => ['Selected data source was not found.']], 404);
            return;
        }

        try {
            $result = $this->service()->review($dsl);
            $errors = array_values(array_map('strval', (array) ($result['errors'] ?? [])));
            if ($errors !== []) {
                $this->wp->sendJson(['ok' => false, 'errors' => $errors], 400);
                return;
            }
            $this->wp->sendJson([
                'ok' => true,
                'source' => $sourceName,
                'rows' => is_array($result['rows'] ?? null) ? $result['rows'] : [],
                'columns' => is_array($result['columns'] ?? null) ? $result['columns'] : [],
                'count' => (int) ($result['count'] ?? 0),
            ]);
        } catch (\Throwable $e) {
            $this->wp->sendJson(['ok' => false, 'errors' => [$e->getMessage()]], 500);
        }
    }

    /** @param list<array<string,mixed>> $sources */
    private function dslForSource(array $sources, string $sourceName): string
    {
        foreach ($sources as $source) {
            $name = trim((string) ($source['name'] ?? ''));
            if ($name === $sourceName) {
                return trim((string) ($source['dsl_text'] ?? ''));
            }
        }
        return '';
    }

    private function service(): DataSourceService
    {
        return ($this->serviceFactory)();
    }
}
