import { Component, createSignal, createMemo, Show, Switch, Match } from 'solid-js';

import { createMutation } from '@tanstack/solid-query';
import ArrowPath from 'heroicons/24/outline/arrow-path.svg';
import EllipsisHorizontal from 'heroicons/24/outline/ellipsis-horizontal.svg';
import GlobeAlt from 'heroicons/24/outline/globe-alt.svg';
import CheckCircle from 'heroicons/24/solid/check-circle.svg';
import ExclamationCircle from 'heroicons/24/solid/exclamation-circle.svg';

import ContextMenu, { MenuItem } from '@/components/ContextMenu';
import TextNoteContentDisplay from '@/components/event/textNote/TextNoteContentDisplay';
import BasicModal from '@/components/modal/BasicModal';
import UserList from '@/components/modal/UserList';
import Timeline from '@/components/timeline/Timeline';
import SafeLink from '@/components/utils/SafeLink';
import { createFollowingColumn, createPostsColumn } from '@/core/column';
import useConfig from '@/core/useConfig';
import { useRequestCommand } from '@/hooks/useCommandBus';
import useModalState from '@/hooks/useModalState';
import { useTranslation } from '@/i18n/useTranslation';
import { genericEvent } from '@/nostr/event';
import parseTextNote, { toResolved } from '@/nostr/parseTextNote';
import useCommands from '@/nostr/useCommands';
import useFollowers from '@/nostr/useFollowers';
import useFollowings, { fetchLatestFollowings } from '@/nostr/useFollowings';
import useProfile from '@/nostr/useProfile';
import usePubkey from '@/nostr/usePubkey';
import useSubscription from '@/nostr/useSubscription';
import useVerification from '@/nostr/useVerification';
import ensureNonNull from '@/utils/ensureNonNull';
import epoch from '@/utils/epoch';
import npubEncodeFallback from '@/utils/npubEncodeFallback';
import timeout from '@/utils/timeout';

export type ProfileDisplayProps = {
  pubkey: string;
  onClose?: () => void;
};

const FollowersCount: Component<{ pubkey: string }> = (props) => {
  const { count } = useFollowers(() => ({
    pubkey: props.pubkey,
  }));

  return <>{count()}</>;
};

const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
  const i18n = useTranslation();
  const { config, addMutedPubkey, removeMutedPubkey, isPubkeyMuted, saveColumn } = useConfig();
  const request = useRequestCommand();
  const commands = useCommands();
  const myPubkey = usePubkey();
  const { showProfileEdit } = useModalState();

  const npub = createMemo(() => npubEncodeFallback(props.pubkey));

  const [updatingContacts, setUpdatingContacts] = createSignal(false);
  const [hoverFollowButton, setHoverFollowButton] = createSignal(false);
  const [showFollowers, setShowFollowers] = createSignal(false);
  const [modal, setModal] = createSignal<'Following' | null>(null);
  const closeModal = () => setModal(null);

  const {
    profile,
    event: profileEvent,
    query: profileQuery,
  } = useProfile(() => ({
    pubkey: props.pubkey,
  }));
  const { verification, query: verificationQuery } = useVerification(() =>
    ensureNonNull([profile()?.nip05] as const)(([nip05]) => ({ nip05 })),
  );
  const nip05Identifier = () => {
    const ident = profile()?.nip05;
    if (ident == null) return null;
    const [user, domain] = ident.split('@');
    if (domain == null) return null;
    if (user === '_') return { domain, ident: domain };
    return { user, domain, ident };
  };
  const isVerified = () => verification()?.pubkey === props.pubkey;
  const isMuted = () => isPubkeyMuted(props.pubkey);

  const aboutParsed = createMemo(() => {
    const ev = profileEvent();
    const about = profile()?.about;
    if (ev == null || about == null) return undefined;

    const parsed = parseTextNote(about);
    const resolved = toResolved(parsed, genericEvent(ev));
    return resolved;
  });

  const {
    followingPubkeys: myFollowingPubkeys,
    invalidateFollowings: invalidateMyFollowings,
    query: myFollowingQuery,
  } = useFollowings(() =>
    ensureNonNull([myPubkey()] as const)(([pubkeyNonNull]) => ({
      pubkey: pubkeyNonNull,
    })),
  );
  const following = () => myFollowingPubkeys().includes(props.pubkey);

  const { followingPubkeys: userFollowingPubkeys, query: userFollowingQuery } = useFollowings(
    () => ({ pubkey: props.pubkey }),
  );

  const followed = () => {
    const p = myPubkey();
    return p != null && userFollowingPubkeys().includes(p);
  };

  const updateContactsMutation = createMutation(() => ({
    mutationKey: ['updateContacts'],
    mutationFn: (...params: Parameters<typeof commands.updateContacts>) =>
      commands
        .updateContacts(...params)
        .then((promises) => Promise.allSettled(promises.map(timeout(5000)))),
    onSuccess: (results) => {
      const succeeded = results.filter((res) => res.status === 'fulfilled').length;
      const failed = results.length - succeeded;
      if (succeeded === results.length) {
        console.log('succeeded to update contacts');
      } else if (succeeded > 0) {
        console.log(
          `succeeded to update contacts for ${succeeded} relays but failed for ${failed} relays`,
        );
      } else {
        console.error('failed to update contacts');
      }
    },
    onError: (err) => {
      console.error('failed to update contacts: ', err);
    },
    onSettled: () => {
      invalidateMyFollowings()
        .then(() => myFollowingQuery.refetch())
        .catch((err) => console.error('failed to refetch contacts', err));
    },
  }));

  const updateContacts = async (op: 'follow' | 'unfollow', pubkey: string) => {
    try {
      const p = myPubkey();
      if (p == null) return;
      setUpdatingContacts(true);

      const latest = await fetchLatestFollowings({ pubkey: p });

      if (
        (latest.data() == null || latest.followingPubkeys().length === 0) &&
        !window.confirm(i18n()('profile.confirmUpdateEvenIfEmpty'))
      )
        return;

      if ((latest?.data()?.created_at ?? 0) < (myFollowingQuery.data?.created_at ?? 0)) {
        window.alert(i18n()('profile.failedToFetchLatestFollowList'));
        return;
      }

      const latestTags = latest.data()?.tags ?? [];
      let updatedTags: string[][];
      switch (op) {
        case 'follow':
          updatedTags = [...latestTags, ['p', pubkey]];
          break;
        case 'unfollow':
          updatedTags = latestTags.filter(([name, value]) => !(name === 'p' && value === pubkey));
          break;
        default:
          console.error('updateContacts: invalid operation', op);
          return;
      }

      await updateContactsMutation.mutateAsync({
        relayUrls: config().relayUrls,
        pubkey: p,
        updatedTags,
        content: latest.data()?.content ?? '',
      });
    } catch (err) {
      console.error('failed to update contact list', err);
      window.alert(i18n()('profile.failedToUpdateFollowList'));
    } finally {
      setUpdatingContacts(false);
    }
  };

  const follow = () => {
    updateContacts('follow', props.pubkey).catch((err) => {
      console.log('failed to follow', err);
    });
  };

  const unfollow = () => {
    if (!window.confirm(i18n()('profile.confirmUnfollow'))) return;

    updateContacts('unfollow', props.pubkey).catch((err) => {
      console.log('failed to unfollow', err);
    });
  };

  const menu: MenuItem[] = [
    /*
    {
      content: () => 'ユーザ宛に投稿',
      onSelect: () => {
        navigator.clipboard.writeText(npub()).catch((err) => window.alert(err));
      },
    },
     */
    {
      content: () => i18n()('profile.copyPubkey'),
      onSelect: () => {
        navigator.clipboard.writeText(npub()).catch((err) => window.alert(err));
      },
    },
    {
      content: () => i18n()('profile.addUserColumn'),
      onSelect: () => {
        const columnName = profile()?.name ?? npub();
        saveColumn(createPostsColumn({ name: columnName, pubkey: props.pubkey }));
        request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
        props.onClose?.();
      },
    },
    {
      content: () => i18n()('profile.addUserHomeColumn'),
      onSelect: () => {
        const columnName = `${i18n()('column.home')} / ${profile()?.name ?? npub()}`;
        saveColumn(createFollowingColumn({ name: columnName, pubkey: props.pubkey }));
        request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
        props.onClose?.();
      },
    },
    {
      content: () => (!isMuted() ? i18n()('profile.mute') : i18n()('profile.unmute')),
      onSelect: () => {
        if (!isMuted()) {
          addMutedPubkey(props.pubkey);
        } else {
          removeMutedPubkey(props.pubkey);
        }
      },
    },
    {
      when: () => props.pubkey === myPubkey(),
      content: () =>
        !following() ? i18n()('profile.followMyself') : i18n()('profile.unfollowMyself'),
      onSelect: () => {
        if (!following()) {
          follow();
        } else {
          unfollow();
        }
      },
    },
  ];

  const { events } = useSubscription(() => ({
    relayUrls: config().relayUrls,
    filters: [
      {
        kinds: [1, 6],
        authors: [props.pubkey],
        limit: 10,
        until: epoch(),
      },
    ],
    continuous: false,
  }));

  return (
    <BasicModal onClose={() => props.onClose?.()}>
      <Show
        when={profileQuery.isFetched && profile()?.banner}
        fallback={<div class="h-12 shrink-0" />}
        keyed
      >
        {(bannerUrl) => (
          <div class="h-40 w-full shrink-0 sm:h-52">
            <img src={bannerUrl} alt="header" class="h-full w-full object-cover" />
          </div>
        )}
      </Show>
      <div class="mt-[-54px] flex items-end gap-4 px-4 pt-4">
        <div class="flex-1 shrink-0">
          <div class="h-28 w-28 overflow-hidden rounded-lg shadow-md">
            <Show when={profileQuery.isFetched && profile()?.picture} keyed>
              {(pictureUrl) => (
                <img src={pictureUrl} alt="user icon" class="h-full w-full object-cover" />
              )}
            </Show>
          </div>
        </div>
        <Show when={myPubkey() != null}>
          <div class="flex shrink-0 flex-col items-center gap-1">
            <div class="flex flex-row justify-start gap-1">
              <Switch>
                <Match when={props.pubkey === myPubkey()}>
                  <button
                    class="rounded-full border border-primary px-4 py-2
                    text-center font-bold text-primary hover:bg-primary hover:text-white sm:w-20"
                    onClick={() => showProfileEdit()}
                  >
                    {i18n()('profile.editProfile')}
                  </button>
                </Match>
                <Match when={updateContactsMutation.isPending || updatingContacts()}>
                  <span class="rounded-full border border-primary px-4 py-2 text-primary sm:text-base">
                    {i18n()('general.updating')}
                  </span>
                </Match>
                <Match when={myFollowingQuery.isPending || myFollowingQuery.isFetching}>
                  <span class="rounded-full border border-primary px-4 py-2 text-primary sm:text-base">
                    {i18n()('general.loading')}
                  </span>
                </Match>
                <Match when={following()}>
                  <button
                    class="rounded-full border border-primary bg-primary px-4 py-2
                    text-center font-bold text-white hover:bg-rose-500 sm:w-36"
                    onMouseEnter={() => setHoverFollowButton(true)}
                    onMouseLeave={() => setHoverFollowButton(false)}
                    onClick={() => unfollow()}
                    disabled={updateContactsMutation.isPending}
                  >
                    <Show when={!hoverFollowButton()} fallback={i18n()('profile.unfollow')}>
                      {i18n()('profile.followingCurrently')}
                    </Show>
                  </button>
                </Match>
                <Match when={!following()}>
                  <button
                    class="w-28 rounded-full border border-primary px-4 py-2 text-primary
                    hover:border-rose-400 hover:text-rose-400"
                    onClick={() => follow()}
                    disabled={updateContactsMutation.isPending}
                  >
                    {i18n()('profile.follow')}
                  </button>
                </Match>
              </Switch>
              <ContextMenu menu={menu}>
                <button
                  class="w-10 rounded-full border border-primary p-2 text-primary
                  hover:border-rose-400 hover:text-rose-400"
                >
                  <EllipsisHorizontal />
                </button>
              </ContextMenu>
            </div>
            <Switch>
              <Match when={userFollowingQuery.isPending}>
                <div class="shrink-0 text-xs">{i18n()('general.loading')}</div>
              </Match>
              <Match when={followed()}>
                <div class="shrink-0 text-xs">{i18n()('profile.followsYou')}</div>
              </Match>
            </Switch>
          </div>
        </Show>
      </div>
      <div class="flex items-start px-4 pt-2">
        <div class="h-16 shrink overflow-hidden">
          <Show when={profileQuery.isPending}>{i18n()('general.loading')}</Show>
          <Show when={(profile()?.display_name?.length ?? 0) > 0}>
            <div class="truncate text-xl font-bold">{profile()?.display_name}</div>
          </Show>
          <div class="flex items-center gap-2">
            <Show when={(profile()?.name?.length ?? 0) > 0}>
              <div class="truncate text-xs">@{profile()?.name}</div>
            </Show>
            <Show when={(profile()?.nip05?.length ?? 0) > 0}>
              <div class="flex items-center text-xs">
                {nip05Identifier()?.ident}
                <Switch
                  fallback={
                    <span class="inline-block h-4 w-4 text-rose-500">
                      <ExclamationCircle />
                    </span>
                  }
                >
                  <Match when={verificationQuery.isPending}>
                    <span class="inline-block h-3 w-3">
                      <ArrowPath />
                    </span>
                  </Match>
                  <Match when={isVerified()}>
                    <span class="inline-block h-4 w-4 text-blue-400">
                      <CheckCircle />
                    </span>
                  </Match>
                </Switch>
              </div>
            </Show>
          </div>
          <div class="flex gap-1">
            <div class="truncate text-xs">{npub()}</div>
          </div>
        </div>
      </div>
      <Show when={aboutParsed()} keyed>
        {(parsed) => (
          <div class="max-h-40 shrink-0 overflow-y-auto whitespace-pre-wrap px-4 py-2 text-sm">
            <TextNoteContentDisplay parsed={parsed} embedding={false} initialHidden />
          </div>
        )}
      </Show>
      <div class="flex border-t px-4 py-2">
        <button class="flex flex-1 flex-col items-start" onClick={() => setModal('Following')}>
          <div class="text-sm">{i18n()('profile.following')}</div>
          <div class="text-xl">
            <Show
              when={userFollowingQuery.isFetched}
              fallback={<span class="text-sm">{i18n()('general.loading')}</span>}
            >
              {userFollowingPubkeys().length}
            </Show>
          </div>
        </button>
        <Show when={!config().hideCount}>
          <div class="flex flex-1 flex-col items-start">
            <div class="text-sm">{i18n()('profile.followers')}</div>
            <div class="text-xl">
              <Show
                when={showFollowers()}
                fallback={
                  <button
                    class="text-sm hover:text-stone-800 hover:underline"
                    onClick={() => setShowFollowers(true)}
                  >
                    {i18n()('profile.loadFollowers')}
                  </button>
                }
                keyed
              >
                <FollowersCount pubkey={props.pubkey} />
              </Show>
            </div>
          </div>
        </Show>
      </div>
      <Show when={(profile()?.website ?? '').length > 0}>
        <ul class="border-t px-5 py-2 text-xs">
          <Show when={profile()?.website} keyed>
            {(website) => (
              <li class="flex items-center gap-1">
                <span class="inline-block h-4 w-4" area-label="website" title="website">
                  <GlobeAlt />
                </span>
                <SafeLink class="text-blue-500 underline" href={website} />
              </li>
            )}
          </Show>
        </ul>
      </Show>
      <Switch>
        <Match when={modal() === 'Following'}>
          <UserList data={userFollowingPubkeys()} pubkeyExtractor={(e) => e} onClose={closeModal} />
        </Match>
      </Switch>
      <ul class="border-t p-1">
        <Timeline events={events()} />
      </ul>
    </BasicModal>
  );
};

export default ProfileDisplay;
