import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { EmptyState } from '../components/shared/EmptyState';
import { StatChip } from '../components/shared/StatChip';
import { CreateLocalUserDialog, ResetPasswordDialog } from '../components/admin/LocalUserDialogs';

interface UserOrg {
  orgId: string;
  orgName: string;
  fetchedAt: string;
}

interface UserRecord {
  id: string;
  email: string;
  name: string | null;
  role: 'admin' | 'user';
  status: 'pending' | 'active' | 'disabled';
  createdAt: string;
  orgs?: UserOrg[];
}

type UserFilter = 'all' | 'admin' | 'user' | 'pending';

const STATUS_TONE: Record<UserRecord['status'], { bg: string; fg: string; border?: string; labelKey: string }> = {
  pending: { bg: 'bg-amber-50 dark:bg-amber-500/15', fg: 'text-amber-700 dark:text-amber-300', border: 'border-amber-100 dark:border-amber-500/30', labelKey: 'users.status.pending' },
  active: { bg: 'bg-emerald-50 dark:bg-emerald-500/15', fg: 'text-emerald-700 dark:text-emerald-300', border: 'border-emerald-100 dark:border-emerald-500/30', labelKey: 'users.status.active' },
  disabled: { bg: 'bg-surface-2', fg: 'text-slate-600', border: 'border-hairline', labelKey: 'users.status.disabled' },
};

const ROLE_TONE: Record<UserRecord['role'], { bg: string; fg: string; border?: string; label: string; descKey: string }> = {
  admin: {
    bg: 'bg-blue-50 dark:bg-blue-500/15',
    fg: 'text-blue-700 dark:text-blue-300',
    border: 'border-blue-100 dark:border-blue-500/30',
    label: 'Admin',
    descKey: 'users.role.adminDesc',
  },
  user: {
    bg: 'bg-surface-2',
    fg: 'text-slate-700',
    border: 'border-hairline',
    label: 'User',
    descKey: 'users.role.userDesc',
  },
};

function formatDate(iso: string | null | undefined): string {
  if (!iso) return '—';
  return new Date(iso).toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo', year: 'numeric', month: '2-digit', day: '2-digit' });
}

function CopyableId({ id, label, mono = true }: { id: string; label?: string; mono?: boolean }) {
  const { t } = useTranslation('admin');
  const [copied, setCopied] = useState(false);
  return (
    <button
      type="button"
      onClick={async (e) => {
        e.stopPropagation();
        try {
          await navigator.clipboard.writeText(id);
          setCopied(true);
          setTimeout(() => setCopied(false), 1200);
        } catch { /* ignore */ }
      }}
      title={t('users.copyTooltip', { label: label ?? t('users.defaultIdLabel') })}
      className="group inline-flex items-center gap-1 text-2xs text-slate-500 hover:text-slate-900 transition-colors min-w-0"
    >
      {label && <span className="text-slate-400 flex-shrink-0">{label}:</span>}
      <span className={`${mono ? 'font-mono' : ''} text-slate-700 truncate`}>{id}</span>
      {copied ? (
        <span className="text-emerald-600 flex-shrink-0">✓</span>
      ) : (
        <svg
          className="w-3 h-3 opacity-40 group-hover:opacity-90 flex-shrink-0"
          viewBox="0 0 16 16"
          fill="none"
          stroke="currentColor"
          strokeWidth="1.5"
          aria-hidden="true"
        >
          <rect x="5" y="5" width="8" height="8" rx="1" />
          <path d="M3 11V4a1 1 0 011-1h7" />
        </svg>
      )}
    </button>
  );
}

function Avatar({ name, email, size = 36 }: { name: string | null; email: string; size?: number }) {
  const source = (name || email || '?').trim();
  const initial = source.charAt(0).toUpperCase();
  let hue = 0;
  for (const c of source) hue = (hue * 31 + c.charCodeAt(0)) % 360;
  const style = {
    width: size,
    height: size,
    background: `hsl(${hue} 60% 92%)`,
    color: `hsl(${hue} 50% 35%)`,
    fontSize: Math.round(size * 0.42),
  };
  return (
    <div
      aria-hidden="true"
      className="inline-flex items-center justify-center rounded-full font-extrabold flex-shrink-0"
      style={style}
    >
      {initial}
    </div>
  );
}

export function UsersPage() {
  const { t } = useTranslation('admin');
  const qc = useQueryClient();
  const { data: users = [], isLoading } = useQuery<UserRecord[]>({
    queryKey: ['admin', 'users'],
    queryFn: async () => {
      const res = await fetch('/api/admin/users');
      if (res.status === 401 || res.status === 403 || res.status === 404) return [];
      if (!res.ok) throw new Error('Failed to fetch users');
      const data = await res.json();
      return Array.isArray(data) ? data : [];
    },
  });

  const [filter, setFilter] = useState<UserFilter>('all');
  const [search, setSearch] = useState('');
  const [activeId, setActiveId] = useState<string | null>(null);
  const [showCreate, setShowCreate] = useState(false);
  const [resetUser, setResetUser] = useState<UserRecord | null>(null);
  // Mobile single-column flow: list ↔ detail toggle. On sm+ both panes
  // are visible side-by-side and this flag is ignored.
  const [mobileShowDetail, setMobileShowDetail] = useState(false);

  const counts = useMemo(() => ({
    all: users.length,
    admin: users.filter(u => u.role === 'admin').length,
    user: users.filter(u => u.role === 'user').length,
    pending: users.filter(u => u.status === 'pending').length,
  }), [users]);

  const filtered = useMemo(() => {
    return users.filter(u => {
      if (filter === 'admin' && u.role !== 'admin') return false;
      if (filter === 'user' && u.role !== 'user') return false;
      if (filter === 'pending' && u.status !== 'pending') return false;
      if (search) {
        const hay = `${u.name ?? ''} ${u.email}`.toLowerCase();
        if (!hay.includes(search.toLowerCase())) return false;
      }
      return true;
    });
  }, [users, filter, search]);

  const active = useMemo(() => {
    if (activeId) {
      const found = users.find(u => u.id === activeId);
      if (found) return found;
    }
    return filtered[0] ?? null;
  }, [users, filtered, activeId]);

  const patchMutation = useMutation({
    mutationFn: async ({ id, body }: { id: string; body: Record<string, unknown> }) => {
      const res = await fetch(`/api/admin/users/${id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!res.ok) throw new Error('Patch failed');
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ['admin', 'users'] }),
  });

  const deleteMutation = useMutation({
    mutationFn: async (id: string) => {
      const res = await fetch(`/api/admin/users/${id}`, { method: 'DELETE' });
      if (!res.ok) throw new Error('Delete failed');
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ['admin', 'users'] }),
  });

  const handleSelect = (id: string) => {
    setActiveId(id);
    setMobileShowDetail(true);
  };
  const handleMobileBack = () => {
    setMobileShowDetail(false);
  };

  return (
    <div className="flex h-full min-h-0">
      <div className={`${mobileShowDetail ? 'hidden sm:flex' : 'flex'} w-full sm:w-[320px] flex-shrink-0 sm:border-r sm:border-hairline bg-canvas p-3 flex-col min-h-0`}>
        <UserListPane
          users={filtered}
          activeId={active?.id ?? null}
          counts={counts}
          filter={filter}
          setFilter={setFilter}
          search={search}
          setSearch={setSearch}
          onSelect={handleSelect}
          onClearFilters={() => { setSearch(''); setFilter('all'); }}
          isLoading={isLoading}
          onCreate={() => setShowCreate(true)}
        />
      </div>
      <div className={`${mobileShowDetail ? 'flex' : 'hidden sm:flex'} flex-1 min-w-0 bg-canvas flex-col`}>
        <UserDetailPane
          user={active}
          onMobileBack={handleMobileBack}
          onPatch={(id, body) => patchMutation.mutate({ id, body })}
          onResetPassword={(u) => setResetUser(u)}
          onDelete={(id) => {
            if (confirm(t('users.deleteConfirm'))) {
              deleteMutation.mutate(id);
              if (activeId === id) setActiveId(null);
              setMobileShowDetail(false);
            }
          }}
        />
      </div>

      {showCreate && (
        <CreateLocalUserDialog
          onClose={() => setShowCreate(false)}
          onCreated={() => qc.invalidateQueries({ queryKey: ['admin', 'users'] })}
        />
      )}
      {resetUser && (
        <ResetPasswordDialog
          userId={resetUser.id}
          email={resetUser.email}
          onClose={() => setResetUser(null)}
        />
      )}
    </div>
  );
}

interface UserListPaneProps {
  users: UserRecord[];
  activeId: string | null;
  counts: { all: number; admin: number; user: number; pending: number };
  filter: UserFilter;
  setFilter: (f: UserFilter) => void;
  search: string;
  setSearch: (s: string) => void;
  onSelect: (id: string) => void;
  onClearFilters: () => void;
  isLoading: boolean;
  onCreate: () => void;
}

function UserListPane({
  users, activeId, counts, filter, setFilter, search, setSearch,
  onSelect, onClearFilters, isLoading, onCreate,
}: UserListPaneProps) {
  const { t } = useTranslation('admin');
  const hasFilters = !!search || filter !== 'all';

  const chips: Array<[UserFilter, string, number]> = [
    ['all', t('users.list.filter.all'), counts.all],
    ['admin', 'Admin', counts.admin],
    ['user', 'User', counts.user],
  ];
  if (counts.pending > 0) chips.push(['pending', t('users.list.filter.pending'), counts.pending]);

  return (
    <div className="flex flex-col h-full overflow-hidden">
      <div className="flex items-center gap-3 text-[10px] text-slate-500 px-0.5 pb-2.5 font-mono tabular-nums">
        <span><span className="font-semibold text-slate-700">{counts.all}</span> {t('users.list.countSuffix')}</span>
        {counts.pending > 0 && (
          <>
            <span aria-hidden="true" className="text-slate-300">·</span>
            <span><span className="font-semibold text-amber-600">{counts.pending}</span> {t('users.list.pendingSuffix')}</span>
          </>
        )}
        <button
          type="button"
          onClick={onCreate}
          className="ml-auto px-2 h-6 rounded-md text-2xs font-semibold text-accent border border-accent/60 hover:bg-accent-soft transition-colors font-sans"
        >
          {t('users.list.createButton')}
        </button>
      </div>

      <div className="flex flex-col gap-2 pb-3 border-b border-hairline">
        <div className="flex items-center gap-1.5 bg-canvas border border-hairline rounded-md pl-2.5 pr-1 h-8">
          <svg aria-hidden="true" className="w-3.5 h-3.5 text-slate-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
          </svg>
          <input
            aria-label={t('users.list.searchAria')}
            value={search}
            onChange={e => setSearch(e.target.value)}
            placeholder={t('users.list.searchPlaceholder')}
            className="flex-1 bg-transparent border-0 outline-none text-[13px] text-slate-900 placeholder:text-slate-400 min-w-0"
          />
        </div>
        <div role="tablist" aria-label={t('users.list.filterAria')} className="flex gap-1 overflow-x-auto pb-1 scrollbar-none">
          {chips.map(([key, label, n]) => (
            <button
              key={key}
              type="button"
              role="tab"
              aria-selected={filter === key}
              onClick={() => setFilter(key)}
              className={`flex-shrink-0 px-2 h-7 rounded text-2xs font-medium border transition-colors whitespace-nowrap focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring ${
                filter === key
                  ? 'border-accent/60 bg-accent-soft text-accent font-semibold'
                  : 'border-hairline bg-canvas text-slate-600 hover:bg-surface'
              }`}
            >
              {label} <span className="text-slate-400 ml-0.5 font-mono tabular-nums">{n}</span>
            </button>
          ))}
        </div>
      </div>

      <div className="flex flex-col gap-1.5 mt-2 overflow-y-auto flex-1 min-h-0 pr-0.5">
        {isLoading && <div className="text-[13px] text-slate-500 px-2 py-3">{t('users.list.loading')}</div>}
        {!isLoading && users.length === 0 && (
          hasFilters ? (
            <EmptyState
              compact
              title={t('users.list.filteredEmptyTitle')}
              hint={t('users.list.filteredEmptyHint')}
              action={
                <button
                  type="button"
                  onClick={onClearFilters}
                  className="px-3 py-1.5 rounded-md text-xs font-semibold bg-canvas border border-hairline text-slate-700 hover:border-hairline transition-colors"
                >
                  {t('users.list.clearFilters')}
                </button>
              }
            />
          ) : (
            <EmptyState
              compact
              title={t('users.list.emptyTitle')}
              hint={t('users.list.emptyHint')}
            />
          )
        )}
        {!isLoading && users.map(u => (
          <UserListItem
            key={u.id}
            user={u}
            active={activeId === u.id}
            onClick={() => onSelect(u.id)}
          />
        ))}
      </div>
    </div>
  );
}

function UserListItem({ user, active, onClick }: { user: UserRecord; active: boolean; onClick: () => void }) {
  const { t } = useTranslation('admin');
  const role = ROLE_TONE[user.role];
  return (
    <button
      type="button"
      onClick={onClick}
      aria-current={active ? 'true' : undefined}
      className={`w-full text-left px-3 py-2.5 rounded-md border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring flex items-center gap-2.5 ${
        active ? 'border-accent/60 bg-accent-soft' : 'border-hairline bg-canvas hover:border-hairline'
      }`}
    >
      <Avatar name={user.name} email={user.email} size={36} />
      <div className="flex-1 min-w-0">
        <div className="flex items-center gap-1.5 min-w-0">
          <span className="flex-1 min-w-0 truncate text-[13px] font-semibold text-slate-900">
            {user.name || t('users.item.nameUnset')}
          </span>
          {user.status === 'pending' && (
            <span className="text-[10px] font-medium px-1 rounded bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-100 dark:border-amber-500/30 flex-shrink-0">{t('users.item.pendingBadge')}</span>
          )}
        </div>
        <div className="text-2xs text-slate-500 truncate">{user.email}</div>
        <div className="mt-1 flex items-center gap-1.5">
          <span className={`text-[10px] font-medium px-1.5 py-0.5 rounded border ${role.bg} ${role.fg} ${role.border ?? 'border-transparent'}`}>
            {role.label}
          </span>
        </div>
      </div>
    </button>
  );
}

interface UserDetailPaneProps {
  user: UserRecord | null;
  onPatch: (id: string, body: Record<string, unknown>) => void;
  onDelete: (id: string) => void;
  onResetPassword: (user: UserRecord) => void;
  /** Mobile-only callback to return to the list pane. Hidden on sm+. */
  onMobileBack?: () => void;
}

function UserDetailPane({ user, onPatch, onDelete, onResetPassword, onMobileBack }: UserDetailPaneProps) {
  const { t } = useTranslation('admin');
  if (!user) {
    return (
      <div className="h-full flex items-center justify-center p-10">
        <EmptyState
          title={t('users.detail.emptyTitle')}
          hint={t('users.detail.emptyHint')}
        />
      </div>
    );
  }

  const status = STATUS_TONE[user.status];

  return (
    <div className="flex flex-col h-full overflow-hidden">
      <div className="flex-shrink-0 px-3 sm:px-5 py-3 sm:py-3.5 border-b border-hairline bg-canvas flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 sm:gap-3">
        <div className="flex items-center gap-2 sm:gap-3 min-w-0">
          {onMobileBack && (
            <button
              type="button"
              onClick={onMobileBack}
              aria-label={t('users.detail.backToList')}
              className="sm:hidden inline-flex items-center justify-center w-8 h-8 -ml-1 rounded-md text-slate-500 hover:text-slate-900 hover:bg-surface-2 transition-colors flex-shrink-0"
            >
              <svg className="w-4 h-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
                <path d="M10 4l-4 4 4 4" />
              </svg>
            </button>
          )}
          <Avatar name={user.name} email={user.email} size={36} />
          <div className="min-w-0">
            <div className="flex items-center gap-1.5 sm:gap-2">
              <div className="text-sm sm:text-sm font-semibold text-slate-900 truncate">{user.name || t('users.detail.nameUnset')}</div>
              <span className={`text-[10px] font-medium px-1.5 py-0.5 rounded border ${status.bg} ${status.fg} ${status.border ?? 'border-transparent'} flex-shrink-0`}>
                {t(status.labelKey)}
              </span>
            </div>
            <div className="text-2xs sm:text-xs text-slate-500 truncate">{user.email}</div>
            <div className="mt-0.5">
              <CopyableId id={user.id} label={t('users.detail.userIdLabel')} />
            </div>
          </div>
        </div>
        <div className="flex items-center gap-1.5 flex-shrink-0 overflow-x-auto scrollbar-none -mx-1 px-1 sm:mx-0 sm:px-0">
          {user.status === 'pending' && (
            <button
              type="button"
              onClick={() => onPatch(user.id, { status: 'active' })}
              className="px-3 h-7 rounded-md text-xs font-semibold bg-emerald-600 hover:bg-emerald-700 text-white transition-colors whitespace-nowrap"
            >
              {t('users.detail.approve')}
            </button>
          )}
          {user.status === 'active' && (
            <button
              type="button"
              onClick={() => onPatch(user.id, { status: 'disabled' })}
              className="px-3 h-7 rounded-md text-xs font-medium bg-canvas border border-amber-200 text-amber-700 dark:text-amber-300 hover:bg-amber-50 dark:hover:bg-amber-500/15 transition-colors whitespace-nowrap"
            >
              {t('users.detail.disable')}
            </button>
          )}
          {user.status === 'disabled' && (
            <button
              type="button"
              onClick={() => onPatch(user.id, { status: 'active' })}
              className="px-3 h-7 rounded-md text-xs font-medium bg-canvas border border-hairline text-slate-700 hover:bg-surface transition-colors whitespace-nowrap"
            >
              {t('users.detail.enable')}
            </button>
          )}
          <button
            type="button"
            onClick={() => onResetPassword(user)}
            className="px-3 h-7 rounded-md text-xs font-medium bg-canvas border border-hairline text-slate-700 hover:bg-surface transition-colors whitespace-nowrap"
          >
            {t('users.detail.password')}
          </button>
          {user.id !== 'local' && (
            <button
              type="button"
              onClick={() => onDelete(user.id)}
              className="px-3 h-7 rounded-md text-xs font-medium bg-canvas border border-red-200 text-red-700 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-500/15 transition-colors whitespace-nowrap"
            >
              {t('users.detail.delete')}
            </button>
          )}
        </div>
      </div>

      <div className="flex-1 overflow-y-auto px-4 sm:px-6 py-4 sm:py-5 bg-surface">
        <div className="max-w-[640px] mx-auto">
          <div className="flex gap-2.5 mb-5 flex-wrap">
            <StatChip label={t('users.detail.stat.role')} value={ROLE_TONE[user.role].label} />
            <StatChip label={t('users.detail.stat.status')} value={t(status.labelKey)} />
            <StatChip label={t('users.detail.stat.registered')} value={formatDate(user.createdAt)} />
            <StatChip label={t('users.detail.stat.orgs')} value={user.orgs?.length ?? 0} />
          </div>

          <div className="bg-canvas border border-hairline rounded-md p-5">
            <div className="section-label mb-3.5">
              {t('users.detail.rolesSectionTitle')}
            </div>
            {(['admin', 'user'] as const).map(r => {
              const selected = user.role === r;
              const tone = ROLE_TONE[r];
              return (
                <button
                  key={r}
                  type="button"
                  onClick={() => !selected && onPatch(user.id, { role: r })}
                  aria-pressed={selected}
                  className={`w-full text-left px-3.5 py-3 rounded-md border mb-2 last:mb-0 flex items-center gap-3 transition-colors ${
                    selected
                      ? 'border-accent/60 bg-accent-soft'
                      : 'border-hairline bg-canvas hover:border-hairline'
                  }`}
                >
                  <span
                    aria-hidden="true"
                    className={`w-[18px] h-[18px] rounded-full flex-shrink-0 border-2 inline-flex items-center justify-center ${
                      selected ? 'border-accent bg-accent' : 'border-hairline bg-canvas'
                    }`}
                  >
                    {selected && (
                      <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={3} strokeLinecap="round" strokeLinejoin="round" className="text-white">
                        <path d="M5 13l4 4L19 7" />
                      </svg>
                    )}
                  </span>
                  <div className="flex-1 min-w-0">
                    <div className="text-[13px] font-semibold text-slate-900">{tone.label}</div>
                    <div className="text-2xs text-slate-500 mt-0.5">{t(tone.descKey)}</div>
                  </div>
                </button>
              );
            })}
          </div>

          <div className="bg-canvas border border-hairline rounded-md p-5 mt-4">
            <div className="section-label mb-3.5">
              {t('users.detail.giteaOrgsSectionTitle')}
            </div>
            {user.orgs && user.orgs.length > 0 ? (
              <div className="flex flex-wrap gap-2">
                {user.orgs.map(o => (
                  <div
                    key={o.orgId}
                    className="inline-flex items-center gap-2 pl-2 pr-1 h-6 rounded border border-hairline bg-surface text-slate-700 text-2xs font-medium"
                  >
                    <span aria-hidden="true" className="w-1.5 h-1.5 rounded-full bg-slate-400" />
                    <span>{o.orgName}</span>
                    <span className="text-slate-300" aria-hidden="true">·</span>
                    <CopyableId id={o.orgId} label={t('users.detail.orgIdLabel')} />
                  </div>
                ))}
              </div>
            ) : (
              <div className="text-xs text-slate-500">{t('users.detail.noOrgs')}</div>
            )}
          </div>

          <div className="h-10" />
        </div>
      </div>
    </div>
  );
}
