import { Match, Switch, createMemo, createSignal, onMount, type JSX } from 'solid-js'

import { dictionary, type Locale } from './i18n'
import {
  approveMiniAppPendingMember,
  fetchMiniAppDashboard,
  fetchMiniAppPendingMembers,
  fetchMiniAppSession,
  joinMiniAppHousehold,
  type MiniAppDashboard,
  type MiniAppPendingMember
} from './miniapp-api'
import { getTelegramWebApp } from './telegram-webapp'

type SessionState =
  | {
      status: 'loading'
    }
  | {
      status: 'blocked'
      reason: 'telegram_only' | 'error'
    }
  | {
      status: 'onboarding'
      mode: 'join_required' | 'pending' | 'open_from_group'
      householdName?: string
      telegramUser: {
        firstName: string | null
        username: string | null
        languageCode: string | null
      }
    }
  | {
      status: 'ready'
      mode: 'live' | 'demo'
      member: {
        displayName: string
        isAdmin: boolean
      }
      telegramUser: {
        firstName: string | null
        username: string | null
        languageCode: string | null
      }
    }

type NavigationKey = 'home' | 'balances' | 'ledger' | 'house'

const demoSession: Extract<SessionState, { status: 'ready' }> = {
  status: 'ready',
  mode: 'demo',
  member: {
    displayName: 'Demo Resident',
    isAdmin: false
  },
  telegramUser: {
    firstName: 'Demo',
    username: 'demo_user',
    languageCode: 'en'
  }
}

function detectLocale(): Locale {
  const telegramLocale = getTelegramWebApp()?.initDataUnsafe?.user?.language_code
  const browserLocale = navigator.language.toLowerCase()

  return (telegramLocale ?? browserLocale).startsWith('ru') ? 'ru' : 'en'
}

function joinContext(): {
  joinToken?: string
  botUsername?: string
} {
  if (typeof window === 'undefined') {
    return {}
  }

  const params = new URLSearchParams(window.location.search)
  const joinToken = params.get('join')?.trim()
  const botUsername = params.get('bot')?.trim()

  return {
    ...(joinToken
      ? {
          joinToken
        }
      : {}),
    ...(botUsername
      ? {
          botUsername
        }
      : {})
  }
}

function joinDeepLink(): string | null {
  const context = joinContext()
  if (!context.botUsername || !context.joinToken) {
    return null
  }

  return `https://t.me/${context.botUsername}?start=join_${encodeURIComponent(context.joinToken)}`
}

function App() {
  const [locale, setLocale] = createSignal<Locale>('en')
  const [session, setSession] = createSignal<SessionState>({
    status: 'loading'
  })
  const [activeNav, setActiveNav] = createSignal<NavigationKey>('home')
  const [dashboard, setDashboard] = createSignal<MiniAppDashboard | null>(null)
  const [pendingMembers, setPendingMembers] = createSignal<readonly MiniAppPendingMember[]>([])
  const [joining, setJoining] = createSignal(false)
  const [approvingTelegramUserId, setApprovingTelegramUserId] = createSignal<string | null>(null)

  const copy = createMemo(() => dictionary[locale()])
  const onboardingSession = createMemo(() => {
    const current = session()
    return current.status === 'onboarding' ? current : null
  })
  const blockedSession = createMemo(() => {
    const current = session()
    return current.status === 'blocked' ? current : null
  })
  const readySession = createMemo(() => {
    const current = session()
    return current.status === 'ready' ? current : null
  })
  const webApp = getTelegramWebApp()

  async function loadDashboard(initData: string) {
    try {
      setDashboard(await fetchMiniAppDashboard(initData))
    } catch (error) {
      if (import.meta.env.DEV) {
        console.warn('Failed to load mini app dashboard', error)
      }

      setDashboard(null)
    }
  }

  async function loadPendingMembers(initData: string) {
    try {
      setPendingMembers(await fetchMiniAppPendingMembers(initData))
    } catch (error) {
      if (import.meta.env.DEV) {
        console.warn('Failed to load pending mini app members', error)
      }

      setPendingMembers([])
    }
  }

  async function bootstrap() {
    setLocale(detectLocale())

    webApp?.ready?.()
    webApp?.expand?.()

    const initData = webApp?.initData?.trim()
    if (!initData) {
      if (import.meta.env.DEV) {
        setSession(demoSession)
        return
      }

      setSession({
        status: 'blocked',
        reason: 'telegram_only'
      })
      return
    }

    try {
      const payload = await fetchMiniAppSession(initData, joinContext().joinToken)
      if (!payload.authorized || !payload.member || !payload.telegramUser) {
        setSession({
          status: 'onboarding',
          mode: payload.onboarding?.status ?? 'open_from_group',
          ...(payload.onboarding?.householdName
            ? {
                householdName: payload.onboarding.householdName
              }
            : {}),
          telegramUser: payload.telegramUser ?? {
            firstName: null,
            username: null,
            languageCode: null
          }
        })
        return
      }

      setSession({
        status: 'ready',
        mode: 'live',
        member: payload.member,
        telegramUser: payload.telegramUser
      })

      await loadDashboard(initData)
      if (payload.member.isAdmin) {
        await loadPendingMembers(initData)
      }
    } catch {
      if (import.meta.env.DEV) {
        setSession(demoSession)
        setDashboard({
          period: '2026-03',
          currency: 'USD',
          totalDueMajor: '820.00',
          members: [
            {
              memberId: 'alice',
              displayName: 'Alice',
              rentShareMajor: '350.00',
              utilityShareMajor: '60.00',
              purchaseOffsetMajor: '-15.00',
              netDueMajor: '395.00',
              explanations: ['Equal utility split', 'Shared purchase offset']
            },
            {
              memberId: 'bob',
              displayName: 'Bob',
              rentShareMajor: '350.00',
              utilityShareMajor: '60.00',
              purchaseOffsetMajor: '15.00',
              netDueMajor: '425.00',
              explanations: ['Equal utility split']
            }
          ],
          ledger: [
            {
              id: 'purchase-1',
              kind: 'purchase',
              title: 'Soap',
              amountMajor: '30.00',
              actorDisplayName: 'Alice',
              occurredAt: '2026-03-12T11:00:00.000Z'
            },
            {
              id: 'utility-1',
              kind: 'utility',
              title: 'Electricity',
              amountMajor: '120.00',
              actorDisplayName: 'Alice',
              occurredAt: '2026-03-12T12:00:00.000Z'
            }
          ]
        })
        setPendingMembers([
          {
            telegramUserId: '555777',
            displayName: 'Mia',
            username: 'mia',
            languageCode: 'ru'
          }
        ])
        return
      }

      setSession({
        status: 'blocked',
        reason: 'error'
      })
    }
  }

  onMount(() => {
    void bootstrap()
  })

  async function handleJoinHousehold() {
    const initData = webApp?.initData?.trim()
    const joinToken = joinContext().joinToken

    if (!initData || !joinToken || joining()) {
      return
    }

    setJoining(true)

    try {
      const payload = await joinMiniAppHousehold(initData, joinToken)
      if (payload.authorized && payload.member && payload.telegramUser) {
        setSession({
          status: 'ready',
          mode: 'live',
          member: payload.member,
          telegramUser: payload.telegramUser
        })
        await loadDashboard(initData)
        if (payload.member.isAdmin) {
          await loadPendingMembers(initData)
        }
        return
      }

      setSession({
        status: 'onboarding',
        mode: payload.onboarding?.status ?? 'pending',
        ...(payload.onboarding?.householdName
          ? {
              householdName: payload.onboarding.householdName
            }
          : {}),
        telegramUser: payload.telegramUser ?? {
          firstName: null,
          username: null,
          languageCode: null
        }
      })
    } catch {
      setSession({
        status: 'blocked',
        reason: 'error'
      })
    } finally {
      setJoining(false)
    }
  }

  async function handleApprovePendingMember(pendingTelegramUserId: string) {
    const initData = webApp?.initData?.trim()
    if (!initData || approvingTelegramUserId()) {
      return
    }

    setApprovingTelegramUserId(pendingTelegramUserId)

    try {
      await approveMiniAppPendingMember(initData, pendingTelegramUserId)
      setPendingMembers((current) =>
        current.filter((member) => member.telegramUserId !== pendingTelegramUserId)
      )
    } finally {
      setApprovingTelegramUserId(null)
    }
  }

  const renderPanel = () => {
    switch (activeNav()) {
      case 'balances':
        return (
          <div class="balance-list">
            <ShowDashboard
              dashboard={dashboard()}
              fallback={<p>{copy().emptyDashboard}</p>}
              render={(data) =>
                data.members.map((member) => (
                  <article class="balance-item">
                    <header>
                      <strong>{member.displayName}</strong>
                      <span>
                        {member.netDueMajor} {data.currency}
                      </span>
                    </header>
                    <p>
                      {copy().shareRent}: {member.rentShareMajor} {data.currency}
                    </p>
                    <p>
                      {copy().shareUtilities}: {member.utilityShareMajor} {data.currency}
                    </p>
                    <p>
                      {copy().shareOffset}: {member.purchaseOffsetMajor} {data.currency}
                    </p>
                  </article>
                ))
              }
            />
          </div>
        )
      case 'ledger':
        return (
          <div class="ledger-list">
            <ShowDashboard
              dashboard={dashboard()}
              fallback={<p>{copy().emptyDashboard}</p>}
              render={(data) =>
                data.ledger.map((entry) => (
                  <article class="ledger-item">
                    <header>
                      <strong>{entry.title}</strong>
                      <span>
                        {entry.amountMajor} {data.currency}
                      </span>
                    </header>
                    <p>{entry.actorDisplayName ?? 'Household'}</p>
                  </article>
                ))
              }
            />
          </div>
        )
      case 'house':
        return readySession()?.member.isAdmin ? (
          <div class="balance-list">
            <article class="balance-item">
              <header>
                <strong>{copy().pendingMembersTitle}</strong>
              </header>
              <p>{copy().pendingMembersBody}</p>
            </article>
            {pendingMembers().length === 0 ? (
              <article class="balance-item">
                <p>{copy().pendingMembersEmpty}</p>
              </article>
            ) : (
              pendingMembers().map((member) => (
                <article class="balance-item">
                  <header>
                    <strong>{member.displayName}</strong>
                    <span>{member.telegramUserId}</span>
                  </header>
                  <p>
                    {member.username
                      ? copy().pendingMemberHandle.replace('{username}', member.username)
                      : (member.languageCode ?? 'Telegram')}
                  </p>
                  <button
                    class="ghost-button"
                    type="button"
                    disabled={approvingTelegramUserId() === member.telegramUserId}
                    onClick={() => void handleApprovePendingMember(member.telegramUserId)}
                  >
                    {approvingTelegramUserId() === member.telegramUserId
                      ? copy().approvingMember
                      : copy().approveMemberAction}
                  </button>
                </article>
              ))
            )}
          </div>
        ) : (
          copy().houseEmpty
        )
      default:
        return (
          <ShowDashboard
            dashboard={dashboard()}
            fallback={<p>{copy().summaryBody}</p>}
            render={(data) => (
              <>
                <p>
                  {copy().totalDue}: {data.totalDueMajor} {data.currency}
                </p>
                <p>{copy().summaryBody}</p>
              </>
            )}
          />
        )
    }
  }

  return (
    <main class="shell">
      <div class="shell__backdrop shell__backdrop--top" />
      <div class="shell__backdrop shell__backdrop--bottom" />

      <section class="topbar">
        <div>
          <p class="eyebrow">{copy().appSubtitle}</p>
          <h1>{copy().appTitle}</h1>
        </div>

        <label class="locale-switch">
          <span>{copy().language}</span>
          <div class="locale-switch__buttons">
            <button
              classList={{ 'is-active': locale() === 'en' }}
              type="button"
              onClick={() => setLocale('en')}
            >
              EN
            </button>
            <button
              classList={{ 'is-active': locale() === 'ru' }}
              type="button"
              onClick={() => setLocale('ru')}
            >
              RU
            </button>
          </div>
        </label>
      </section>

      <Switch>
        <Match when={session().status === 'loading'}>
          <section class="hero-card">
            <span class="pill">{copy().navHint}</span>
            <h2>{copy().loadingTitle}</h2>
            <p>{copy().loadingBody}</p>
          </section>
        </Match>

        <Match when={session().status === 'blocked'}>
          <section class="hero-card">
            <span class="pill">{copy().navHint}</span>
            <h2>
              {blockedSession()?.reason === 'telegram_only'
                ? copy().telegramOnlyTitle
                : copy().unexpectedErrorTitle}
            </h2>
            <p>
              {blockedSession()?.reason === 'telegram_only'
                ? copy().telegramOnlyBody
                : copy().unexpectedErrorBody}
            </p>
            <button class="ghost-button" type="button" onClick={() => window.location.reload()}>
              {copy().reload}
            </button>
          </section>
        </Match>

        <Match when={session().status === 'onboarding'}>
          <section class="hero-card">
            <span class="pill">{copy().navHint}</span>
            <h2>
              {onboardingSession()?.mode === 'pending'
                ? copy().pendingTitle
                : onboardingSession()?.mode === 'open_from_group'
                  ? copy().openFromGroupTitle
                  : copy().joinTitle}
            </h2>
            <p>
              {onboardingSession()?.mode === 'pending'
                ? copy().pendingBody.replace(
                    '{household}',
                    onboardingSession()?.householdName ?? copy().householdFallback
                  )
                : onboardingSession()?.mode === 'open_from_group'
                  ? copy().openFromGroupBody
                  : copy().joinBody.replace(
                      '{household}',
                      onboardingSession()?.householdName ?? copy().householdFallback
                    )}
            </p>
            <div class="nav-grid">
              {onboardingSession()?.mode === 'join_required' ? (
                <button
                  class="ghost-button"
                  type="button"
                  disabled={joining()}
                  onClick={handleJoinHousehold}
                >
                  {joining() ? copy().joining : copy().joinAction}
                </button>
              ) : null}
              {joinDeepLink() ? (
                <a
                  class="ghost-button"
                  href={joinDeepLink() ?? '#'}
                  target="_blank"
                  rel="noreferrer"
                >
                  {copy().botLinkAction}
                </a>
              ) : null}
              <button class="ghost-button" type="button" onClick={() => window.location.reload()}>
                {copy().reload}
              </button>
            </div>
          </section>
        </Match>

        <Match when={session().status === 'ready'}>
          <section class="hero-card">
            <div class="hero-card__meta">
              <span class="pill">
                {readySession()?.mode === 'demo' ? copy().demoBadge : copy().navHint}
              </span>
              <span class="pill pill--muted">
                {readySession()?.member.isAdmin ? copy().adminTag : copy().residentTag}
              </span>
            </div>

            <h2>
              {copy().welcome},{' '}
              {readySession()?.telegramUser.firstName ?? readySession()?.member.displayName}
            </h2>
            <p>{copy().sectionBody}</p>
          </section>

          <nav class="nav-grid">
            {(
              [
                ['home', copy().home],
                ['balances', copy().balances],
                ['ledger', copy().ledger],
                ['house', copy().house]
              ] as const
            ).map(([key, label]) => (
              <button
                classList={{ 'is-active': activeNav() === key }}
                type="button"
                onClick={() => setActiveNav(key)}
              >
                {label}
              </button>
            ))}
          </nav>

          <section class="content-grid">
            <article class="panel panel--wide">
              <p class="eyebrow">{copy().summaryTitle}</p>
              <h3>{readySession()?.member.displayName}</h3>
              <div>{renderPanel()}</div>
            </article>

            <article class="panel">
              <p class="eyebrow">{copy().cardAccess}</p>
              <p>{copy().cardAccessBody}</p>
            </article>

            <article class="panel">
              <p class="eyebrow">{copy().cardLocale}</p>
              <p>{copy().cardLocaleBody}</p>
            </article>

            <article class="panel">
              <p class="eyebrow">{copy().cardNext}</p>
              <p>{copy().cardNextBody}</p>
            </article>
          </section>
        </Match>
      </Switch>
    </main>
  )
}

function ShowDashboard(props: {
  dashboard: MiniAppDashboard | null
  fallback: JSX.Element
  render: (dashboard: MiniAppDashboard) => JSX.Element
}) {
  return <>{props.dashboard ? props.render(props.dashboard) : props.fallback}</>
}

export default App
