<?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"><h1>Review Recipients</h1>';
        echo '<p>Inspect recipient rows for a selected data source with full-field filtering and sorting.</p>';

        echo '<div style="padding:12px;border:1px solid #dcdcde;background:#fff;margin-bottom:12px;">';
        echo '<div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;">';
        echo '<div style="display:flex;align-items:center;gap:8px;padding-top:6px;min-width:320px;">';
        echo '<label for="rr_data_source" style="white-space:nowrap;padding-left:4px;"><strong>Data Source</strong></label>';
        echo '<select id="rr_data_source" style="min-width:220px;">';
        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 style="display:flex;align-items:center;gap:8px;padding-top:6px;min-width:360px;">';
        echo '<label for="rr_filter" style="white-space:nowrap;padding-left:4px;"><strong>Filter by</strong></label>';
        echo '<input id="rr_filter" class="regular-text" type="text" placeholder="Matches any field" style="min-width:240px;">';
        echo '</div>';
        echo '<div style="display:flex;align-items:center;gap:8px;padding-top:6px;min-width:260px;">';
        echo '<label for="rr_sort_by" style="white-space:nowrap;padding-left:4px;"><strong>Sort by</strong></label>';
        echo '<select id="rr_sort_by" style="min-width:170px;"><option value="">(no fields)</option></select>';
        echo '</div>';
        echo '<div style="display:flex;align-items:center;gap:8px;padding-top:6px;min-width:280px;">';
        echo '<label for="rr_sort_direction" style="white-space:nowrap;padding-left:4px;"><strong>Sort Direction</strong></label>';
        echo '<select id="rr_sort_direction" style="min-width:140px;"><option value="asc">Ascending</option><option value="desc">Descending</option></select>';
        echo '</div>';
        echo '</div></div>';

        echo '<div id="rr_status" style="padding:10px;border:1px solid #dcdcde;background:#fff;margin-bottom:12px;">';
        echo 'Select a data source to load recipients.';
        echo '</div>';
        echo '<div id="rr_error" style="display:none;padding:10px;border:1px solid #ef9a9a;background:#ffebee;margin-bottom:12px;"></div>';
        echo '<div style="border:1px solid #dcdcde;background:#fff;max-width:calc(100vw - 80px);">';
        echo '<div id="rr_scroll" style="overflow:scroll;max-height:68vh;scrollbar-gutter:stable both-edges;">';
        echo '<table class="widefat striped" id="rr_table" style="min-width:100%;width:max-content;margin:0;border-collapse:separate;border-spacing:0;">';
        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 state={source:"",filter:"",sortBy:"",sortDirection:"asc",selectedRowIndex:-1};';
        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.style.display="none";errEl.textContent="";}';
        echo 'function showErr(msg){errEl.style.display="block";errEl.textContent=msg||"Unknown error";}';
        echo 'function status(msg){statusEl.textContent=msg||"";}';
        echo 'function setColumnWidths(cols){var widthPx=170;var c="";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 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 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||[]).slice();var cols=normalizeColumns(payload.columns||[],rows);if(cols.length===0){setColumnWidths([]);headRow.innerHTML="<th>No fields</th>";bodyEl.innerHTML="";status("Rows: 0 | Total from query: "+(payload.count||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 'if(state.selectedRowIndex>=filtered.length){state.selectedRowIndex=-1;}setColumnWidths(cols);headRow.innerHTML="";if(cols.length===0){headRow.innerHTML="<th>No fields</th>";bodyEl.innerHTML="";status("Rows: "+filtered.length+" | Total from query: "+(payload.count||0));return;}cols.forEach(function(c){var th=document.createElement("th");th.style.position="sticky";th.style.top="0";th.style.zIndex="2";th.style.background="#f6f7f7";th.style.whiteSpace="normal";th.style.verticalAlign="bottom";th.style.maxWidth="170px";th.style.padding="8px";var p=headerParts(c);if(p.prefix){th.innerHTML="<div style=\\"line-height:1.2\\"><span style=\\"display:block;font-size:11px;color:#646970;word-break:break-word\\">"+p.prefix+"</span><span style=\\"display:block;font-size:12px;font-weight:600;word-break:break-word\\">"+p.field+"</span></div>";}else{th.innerHTML="<div style=\\"line-height:1.2\\"><span style=\\"display:block;font-size:12px;font-weight:600;word-break:break-word\\">"+p.field+"</span></div>";}headRow.appendChild(th);});bodyEl.innerHTML="";filtered.forEach(function(r,rowIndex){var tr=document.createElement("tr");tr.setAttribute("data-row-index",String(rowIndex));if(rowIndex===state.selectedRowIndex){tr.style.background="#e8f4ff";}cols.forEach(function(c){var td=document.createElement("td");var v=text((r||{})[c]);td.style.whiteSpace="nowrap";td.style.maxWidth="20ch";td.style.width="20ch";td.style.minWidth="20ch";td.style.overflow="hidden";td.style.textOverflow="ellipsis";td.title=v;td.textContent=v;if(rowIndex===state.selectedRowIndex){td.style.background="#e8f4ff";}tr.appendChild(td);});bodyEl.appendChild(tr);});status("Rows: "+filtered.length+" | Total from query: "+(payload.count||0)+(state.filter?(" | Filter: "+state.filter):""));}';
        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;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 'bodyEl.addEventListener("click",function(ev){var node=ev.target;while(node&&node.tagName!=="TR"){node=node.parentNode;}if(!node){return;}var idx=parseInt(node.getAttribute("data-row-index")||"-1",10);if(!isNaN(idx)&&idx>=0){state.selectedRowIndex=idx;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 '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)();
    }
}
