/**
 * The phone's way into search (#57): one button, in the row that
 * already says which page you are on.
 *
 * **Which views show it is not a decision this component makes.**
 * `store/search-store.ts` has held the map of what each view searches
 * since plan 007, and #57's own Findings say so — "that is exactly the
 * condition for showing the button". So this asks `isSearchableView`
 * and renders nothing otherwise, and no second list of searchable views
 * exists to fall out of step with the first.
 *
 * **It is an element rather than a `PageAction`**, and that is the
 * whole reason it is a component at all. Two of the seven searchable
 * views — `playlist-details` and `smart-playlist-details` — have no
 * `page-header`; they filter on the term and say so in their own
 * headers. Declaring search as an action would mean seven hosts each
 * writing it out, which is the second list again, and it would put a
 * *phone mode for actions* inside `page-header`, which that component
 * documents its refusal to grow. An element three headers place is one
 * statement of the rule, placed three times.
 *
 * It does not participate in `page-header`'s overflow measurement, for
 * the reason the count and the sort control do not: it is 32px, it is
 * `flex-shrink: 0`, and the header's `fits()` sees its width like any
 * other child. What it must never do is collapse into the overflow
 * menu — on a phone that menu is the only home for the page's actions
 * already, and search would be two taps behind an ellipsis.
 */
import { LitElement, css, html, nothing } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import '@awesome.me/webawesome/dist/components/icon/icon.js';

import { designTokens } from '../../styles/tokens.css';
import { PHONE_QUERY } from '@utils/breakpoints';
import { SearchController } from '@store/controllers/search-controller';
import { ICON_SEARCH } from '@utils/icon-language';
import { OPEN_SEARCH_EVENT } from './search-dialog';

@customElement('search-trigger')
export class SearchTrigger extends LitElement {
    private searchCtrl = new SearchController(this);

    /**
     * From `matchMedia` rather than a media query, because this decides
     * whether the button *exists* — `job-band`'s rule, and for the same
     * consequence: a header that renders it at every width puts a
     * second search affordance beside the desktop's own box.
     */
    @state() private phone = false;

    private media?: MediaQueryList;

    static override styles = [
        designTokens,
        css`
            :host {
                display: contents;
            }

            button {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                /* The smallest a touch target should be. The header's
                   own action buttons are smaller because they carry a
                   label; this one is a glyph. */
                min-width: 40px;
                min-height: 40px;
                padding: 0;
                background: none;
                border: 1px solid var(--yj-border-subtle, #555);
                border-radius: 4px;
                color: var(--yj-text-primary, #fff);
                cursor: pointer;
                flex-shrink: 0;
            }

            button:focus-visible {
                outline: 2px solid var(--yj-accent, #ffd43b);
                outline-offset: -1px;
            }

            /* A search that is *on* says so without a second control:
               the page already carries "Showing albums matching ...",
               and this is the button that reopens the box to change or
               clear it. */
            button.filtering {
                border-color: var(--yj-accent, #ffd43b);
                color: var(--yj-accent-text, #ffd43b);
            }
        `,
    ];

    override connectedCallback(): void {
        super.connectedCallback();

        this.media = window.matchMedia(PHONE_QUERY);
        this.phone = this.media.matches;
        this.media.addEventListener('change', this.onMedia);
    }

    override disconnectedCallback(): void {
        super.disconnectedCallback();
        this.media?.removeEventListener('change', this.onMedia);
    }

    private onMedia = (e: MediaQueryListEvent): void => {
        this.phone = e.matches;
    };

    private onClick = (): void => {
        document.dispatchEvent(new CustomEvent(OPEN_SEARCH_EVENT));
    };

    override render() {
        if (!this.phone || !this.searchCtrl.isSearchableView) return nothing;

        const scope = this.searchCtrl.scopeLabel;
        const term = this.searchCtrl.term;

        // The name carries the state, because the colour cannot: a
        // control that is a different colour and the same word is a
        // control that says nothing to anyone not seeing it. Same rule
        // `library-status.ts` states for a partial badge.
        const label = term
            ? `Search ${scope}, showing matches for ${term}`
            : `Search ${scope}`;

        return html`
            <button
                data-testid="search-trigger"
                class=${term ? 'filtering' : ''}
                aria-label=${label}
                title=${label}
                @click=${this.onClick}
            >
                <wa-icon name=${ICON_SEARCH}></wa-icon>
            </button>
        `;
    }
}

declare global {
    interface HTMLElementTagNameMap {
        'search-trigger': SearchTrigger;
    }
}
