import { h } from 'preact';
import type { Me } from './types';

// Visibility scope tokens shared by every user-owned, filterable object
// (config sets/instances, synthetic signals, control-logic graphs, panels).
// They mirror internal/access/scope.go. An empty/unknown token is treated as
// global so legacy objects without a scope stay visible to everyone.
export type Scope = 'private' | 'group' | 'global';

// Bucket is the per-tree selector position: which slice of the visible objects
// the user is currently looking at.
export type Bucket = 'mine' | 'group' | 'global';

// Scoped is the minimal shape the scope helpers read off any list item. The
// scope-groups list is not part of this shape because subsystems disagree on
// its field name (config/synthetic use `groups`, control-logic uses
// `scopeGroups`); filterByScope takes an explicit accessor for it instead.
export interface Scoped {
  owner?: string;
  scope?: string;
}

// normalizeScope coerces a raw scope string into a known token; empty/unknown
// becomes 'global' to match the backend's legacy-safe default.
export function normalizeScope(s?: string): Scope {
  switch ((s ?? '').trim().toLowerCase()) {
    case 'private': return 'private';
    case 'group': return 'group';
    default: return 'global';
  }
}

// bucketOf assigns an item to exactly one selector bucket from the caller's
// perspective: items they own go to 'mine'; otherwise group-scoped items go to
// 'group' and everything else (global/empty/unknown) to 'global'.
export function bucketOf(item: Scoped, me: Me): Bucket {
  if (item.owner && item.owner === me.user) return 'mine';
  if (normalizeScope(item.scope) === 'group') return 'group';
  return 'global';
}

// filterByScope keeps the items belonging to the active bucket. In group mode a
// non-empty group narrows to items shared with that specific group; groupsOf
// reads each item's scope-groups (defaulting to its `groups` field).
export function filterByScope<T extends Scoped>(
  items: T[], me: Me, bucket: Bucket, group: string,
  groupsOf: (t: T) => string[] = (t) => (t as any).groups ?? [],
): T[] {
  return items.filter(it => {
    if (bucketOf(it, me) !== bucket) return false;
    if (bucket === 'group' && group) return groupsOf(it).includes(group);
    return true;
  });
}

// ScopeFilter is the segmented [Mine | Group ▾ | Global] selector shown above a
// filterable tree. The Group segment carries a combo to pick which group when
// the user belongs to more than one; it is disabled for users with no groups.
export function ScopeFilter({ me, value, onChange, group, onGroupChange }: {
  me: Me;
  value: Bucket;
  onChange: (b: Bucket) => void;
  group: string;
  onGroupChange: (g: string) => void;
}) {
  const hasGroups = me.groups.length > 0;
  return (
    <div class="scope-filter">
      <div class="scope-segs">
        <button class={`scope-seg${value === 'mine' ? ' scope-seg-active' : ''}`}
          onClick={() => onChange('mine')}>Mine</button>
        <button class={`scope-seg${value === 'group' ? ' scope-seg-active' : ''}`}
          disabled={!hasGroups} title={hasGroups ? 'Shared with a group' : 'You are not in any group'}
          onClick={() => onChange('group')}>Group</button>
        <button class={`scope-seg${value === 'global' ? ' scope-seg-active' : ''}`}
          onClick={() => onChange('global')}>Global</button>
      </div>
      {value === 'group' && hasGroups && (
        <select class="scope-group-select" value={group}
          onChange={(e) => onGroupChange((e.target as HTMLSelectElement).value)}>
          <option value="">All my groups</option>
          {me.groups.map(g => <option key={g} value={g}>{g}</option>)}
        </select>
      )}
    </div>
  );
}

// ScopePicker is the visibility editor used in create/save flows: a scope select
// plus, for group scope, checkboxes over the user's groups. It calls onChange
// with both the scope token and the chosen groups so callers persist them
// together.
export function ScopePicker({ me, scope, groups, onChange, disabled }: {
  me: Me;
  scope: Scope;
  groups: string[];
  onChange: (scope: Scope, groups: string[]) => void;
  disabled?: boolean;
}) {
  const toggle = (g: string) => {
    const next = groups.includes(g) ? groups.filter(x => x !== g) : [...groups, g];
    onChange('group', next);
  };
  return (
    <div class="scope-picker">
      <label class="scope-picker-label">Visibility</label>
      <select class="prop-input scope-picker-select" value={scope} disabled={disabled}
        onChange={(e) => onChange((e.target as HTMLSelectElement).value as Scope, groups)}>
        <option value="private">Private (only me)</option>
        <option value="group" disabled={me.groups.length === 0}>Group</option>
        <option value="global">Global (everyone)</option>
      </select>
      {scope === 'group' && (
        <div class="scope-picker-groups">
          {me.groups.length === 0 && <span class="hint">You are not in any group.</span>}
          {me.groups.map(g => (
            <label key={g} class="scope-picker-chk">
              <input type="checkbox" checked={groups.includes(g)} disabled={disabled}
                onChange={() => toggle(g)} />{g}
            </label>
          ))}
        </div>
      )}
    </div>
  );
}
