import {
  ControlOutlined,
  DeploymentUnitOutlined,
  HistoryOutlined,
  ReloadOutlined,
  SettingOutlined
} from '@ant-design/icons';
import {
  Alert,
  Button,
  Card,
  Descriptions,
  Empty,
  Form,
  Input,
  Layout,
  Menu,
  Result,
  Select,
  Space,
  Spin,
  Table,
  Tag,
  TimePicker,
  Typography
} from 'antd';
import dayjs, { type Dayjs } from 'dayjs';
import { type ReactNode, useMemo, useState } from 'react';
import { BrowserRouter, Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import { ApiError } from './api/client';
import {
  loadDashboard,
  loadRecentChanges,
  setServiceWindow,
  type ConfigChange,
  type FeatureFlagState,
  type RecentAdminChangesResponse,
  type RestaurantAdminConfigResponse,
  type ServiceWindow,
  type SetServiceWindowRequest,
  type SetServiceWindowResponse
} from './api/dashboardApi';
import type { IdentityProvider } from './api/sessionApi';
import { buildGoogleOidcStartUrl } from './auth/oidcLogin';
import { SessionProvider, useSessionContext } from './auth/sessionContext';

type AppRoute = '/config' | '/window' | '/session';

type WorkflowState = {
  error: string | null;
  sessionExpired: boolean;
};

type ServiceWindowFormValues = {
  contextId: string;
  day: number;
  openAt: Dayjs;
  closeAt: Dayjs;
  updatedBy: string;
};

const routeItems: Array<{ key: AppRoute; label: string; icon: ReactNode }> = [
  { key: '/config', label: 'Config', icon: <SettingOutlined /> },
  { key: '/window', label: 'Service Window', icon: <ControlOutlined /> },
  { key: '/session', label: 'Session', icon: <DeploymentUnitOutlined /> }
];

const dayOptions = [
  { label: 'Sunday', value: 0 },
  { label: 'Monday', value: 1 },
  { label: 'Tuesday', value: 2 },
  { label: 'Wednesday', value: 3 },
  { label: 'Thursday', value: 4 },
  { label: 'Friday', value: 5 },
  { label: 'Saturday', value: 6 }
];

function App() {
  return (
    <SessionProvider>
      <BrowserRouter>
        <RestaurantAdminShell />
      </BrowserRouter>
    </SessionProvider>
  );
}

function RestaurantAdminShell() {
  const session = useSessionContext();
  const location = useLocation();
  const navigate = useNavigate();

  const [contextId, setContextId] = useState('demo-context');
  const [configPayload, setConfigPayload] = useState<RestaurantAdminConfigResponse | null>(null);
  const [changesPayload, setChangesPayload] = useState<RecentAdminChangesResponse | null>(null);
  const [windowResponse, setWindowResponse] = useState<SetServiceWindowResponse | null>(null);
  const [windowHistory, setWindowHistory] = useState<SetServiceWindowResponse[]>([]);
  const [workflowState, setWorkflowState] = useState<WorkflowState>({ error: null, sessionExpired: false });
  const [loadingConfig, setLoadingConfig] = useState(false);
  const [loadingChanges, setLoadingChanges] = useState(false);
  const [submittingWindow, setSubmittingWindow] = useState(false);

  const loginUrl = useMemo(() => buildGoogleOidcStartUrl(window.location.href), []);
  const selectedKey = useMemo(() => {
    const candidate = routeItems.find((item) => location.pathname.startsWith(item.key));
    return candidate?.key ?? '/config';
  }, [location.pathname]);

  const recentChanges = changesPayload?.recentChanges ?? configPayload?.recentChanges ?? [];

  const clearWorkflowError = () => setWorkflowState({ error: null, sessionExpired: false });

  const handleWorkflowFailure = async (err: unknown, fallbackMessage: string) => {
    if (err instanceof ApiError && err.status === 401) {
      setWorkflowState({
        error: 'Your session expired. Sign in again to continue restaurant administration work.',
        sessionExpired: true
      });
      await session.revalidate();
      return;
    }

    setWorkflowState({
      error: err instanceof Error ? err.message : fallbackMessage,
      sessionExpired: false
    });
  };

  const loadConfig = async () => {
    setLoadingConfig(true);
    clearWorkflowError();
    try {
      const payload = await loadDashboard(contextId);
      setConfigPayload(payload);
    } catch (err) {
      await handleWorkflowFailure(err, 'Failed to load restaurant admin configuration.');
    } finally {
      setLoadingConfig(false);
    }
  };

  const refreshChanges = async () => {
    setLoadingChanges(true);
    clearWorkflowError();
    try {
      const payload = await loadRecentChanges(contextId);
      setChangesPayload(payload);
    } catch (err) {
      await handleWorkflowFailure(err, 'Failed to load recent configuration changes.');
    } finally {
      setLoadingChanges(false);
    }
  };

  const applyServiceWindow = async (values: ServiceWindowFormValues) => {
    setSubmittingWindow(true);
    clearWorkflowError();

    const request: SetServiceWindowRequest = {
      contextId: values.contextId,
      day: values.day,
      openAt: `${values.openAt.format('HH:mm')}:00`,
      closeAt: `${values.closeAt.format('HH:mm')}:00`,
      updatedBy: values.updatedBy
    };

    try {
      const payload = await setServiceWindow(request);
      setWindowResponse(payload);
      setWindowHistory((previous) => [payload, ...previous].slice(0, 8));
      await loadConfig();
      await refreshChanges();
    } catch (err) {
      await handleWorkflowFailure(err, 'Failed to set service window.');
    } finally {
      setSubmittingWindow(false);
    }
  };

  if (session.status === 'loading') {
    return (
      <div className="fullscreen-center">
        <Spin size="large" />
      </div>
    );
  }

  if (session.status !== 'authenticated' || !session.profile) {
    return (
      <main className="app">
        <Result
          status="403"
          title="Authentication Required"
          subTitle="Sign in through the central auth host to access restaurant administration workflows."
          extra={
            <Button type="primary" href={loginUrl}>
              Continue with Google
            </Button>
          }
        />
        {workflowState.sessionExpired && (
          <Alert
            type="warning"
            showIcon
            message="Session expired"
            description="Sign in again through the central auth host before retrying control-plane changes."
          />
        )}
        {session.error && <Alert type="error" showIcon message={session.error} />}
      </main>
    );
  }

  return (
    <Layout className="full-layout">
      <Layout.Sider width={240} breakpoint="lg" collapsedWidth={0}>
        <div className="brand">Restaurant Admin Web</div>
        <Menu
          mode="inline"
          selectedKeys={[selectedKey]}
          items={routeItems}
          onClick={(event) => navigate(event.key as AppRoute)}
        />
      </Layout.Sider>
      <Layout>
        <Layout.Header className="header">
          <Space wrap>
            <Tag color="blue">subject: {session.profile.subjectId}</Tag>
            <Tag color="geekblue">tenant: {session.profile.tenantId}</Tag>
            <Tag color="purple">provider: {providerLabel(session.profile.provider)}</Tag>
          </Space>
          <Space>
            <Button icon={<ReloadOutlined />} onClick={() => void session.refresh()}>
              Refresh Session
            </Button>
            <Button danger onClick={() => void session.logout()}>
              Logout
            </Button>
          </Space>
        </Layout.Header>
        <Layout.Content className="content">
          <Typography.Title level={3}>Restaurant Administration</Typography.Title>
          <Typography.Paragraph type="secondary">
            Protected control-plane workflows for configuration snapshots, change history, and service-window management.
          </Typography.Paragraph>
          {session.error && <Alert className="stack-gap" type="warning" showIcon message={session.error} />}
          {workflowState.error && <Alert className="stack-gap" type="error" showIcon message={workflowState.error} />}
          {workflowState.sessionExpired && (
            <Alert
              className="stack-gap"
              type="warning"
              showIcon
              message="Session expired"
              description="Refresh your session or sign in again before retrying admin operations."
              action={
                <Space>
                  <Button size="small" onClick={() => void session.refresh()}>
                    Refresh Session
                  </Button>
                  <Button size="small" type="primary" href={loginUrl}>
                    Reauthenticate
                  </Button>
                </Space>
              }
            />
          )}

          <Routes>
            <Route
              path="/config"
              element={
                <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                  <Card title="Configuration Snapshot">
                    <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                      <Space wrap>
                        <Input
                          value={contextId}
                          onChange={(event) => setContextId(event.target.value)}
                          placeholder="Context Id"
                          style={{ width: 280 }}
                        />
                        <Button type="primary" loading={loadingConfig} onClick={() => void loadConfig()}>
                          Load Config
                        </Button>
                        <Button icon={<HistoryOutlined />} loading={loadingChanges} onClick={() => void refreshChanges()}>
                          Load Recent Changes
                        </Button>
                      </Space>
                      {configPayload ? (
                        <Descriptions bordered size="small" column={1}>
                          <Descriptions.Item label="Context Id">{configPayload.contextId}</Descriptions.Item>
                          <Descriptions.Item label="Version">{configPayload.version}</Descriptions.Item>
                          <Descriptions.Item label="Summary">{configPayload.summary}</Descriptions.Item>
                        </Descriptions>
                      ) : (
                        <Empty description="Load a configuration snapshot to inspect flags and service windows." />
                      )}
                    </Space>
                  </Card>

                  <Card title="Feature Flags">
                    <Table<FeatureFlagState>
                      pagination={false}
                      rowKey={(record) => record.key}
                      dataSource={configPayload?.featureFlags ?? []}
                      locale={{ emptyText: 'No feature flags loaded.' }}
                      columns={[
                        { title: 'Key', dataIndex: 'key' },
                        {
                          title: 'Enabled',
                          render: (_, record) => (
                            <Tag color={record.enabled ? 'green' : 'default'}>{record.enabled ? 'Enabled' : 'Disabled'}</Tag>
                          )
                        }
                      ]}
                    />
                  </Card>

                  <Card title="Service Windows">
                    <Table<ServiceWindow>
                      pagination={false}
                      rowKey={(record) => `${record.day}-${record.openAt}-${record.closeAt}`}
                      dataSource={configPayload?.serviceWindows ?? []}
                      locale={{ emptyText: 'No service windows loaded.' }}
                      columns={[
                        { title: 'Day', render: (_, record) => dayLabel(record.day) },
                        { title: 'Open At', dataIndex: 'openAt' },
                        { title: 'Close At', dataIndex: 'closeAt' },
                        {
                          title: 'Status',
                          render: (_, record) => (
                            <Tag color={record.isClosed ? 'red' : 'green'}>{record.isClosed ? 'Closed' : 'Open'}</Tag>
                          )
                        }
                      ]}
                    />
                  </Card>

                  <Card title="Recent Changes">
                    <Table<ConfigChange>
                      pagination={false}
                      rowKey={(record) => record.changeId}
                      dataSource={recentChanges}
                      locale={{ emptyText: 'No recent changes loaded.' }}
                      columns={[
                        { title: 'Change Id', dataIndex: 'changeId' },
                        { title: 'Category', dataIndex: 'category' },
                        { title: 'Description', dataIndex: 'description' },
                        { title: 'Updated By', dataIndex: 'updatedBy' },
                        { title: 'Updated At', render: (_, record) => formatUtc(record.updatedAtUtc) }
                      ]}
                    />
                  </Card>
                </Space>
              }
            />
            <Route
              path="/window"
              element={
                <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                  <Card title="Service Window Action">
                    <Form<ServiceWindowFormValues>
                      layout="vertical"
                      initialValues={{
                        contextId,
                        day: 1,
                        openAt: dayjs('08:00', 'HH:mm'),
                        closeAt: dayjs('22:00', 'HH:mm'),
                        updatedBy: 'admin-operator'
                      }}
                      onFinish={(values) => void applyServiceWindow(values)}
                    >
                      <Form.Item name="contextId" label="Context Id" rules={[{ required: true }]}>
                        <Input placeholder="Context Id" />
                      </Form.Item>
                      <Form.Item name="day" label="Day Of Week" rules={[{ required: true }]}>
                        <Select options={dayOptions} />
                      </Form.Item>
                      <Form.Item name="openAt" label="Open At" rules={[{ required: true }]}>
                        <TimePicker format="HH:mm" style={{ width: '100%' }} />
                      </Form.Item>
                      <Form.Item name="closeAt" label="Close At" rules={[{ required: true }]}>
                        <TimePicker format="HH:mm" style={{ width: '100%' }} />
                      </Form.Item>
                      <Form.Item name="updatedBy" label="Updated By" rules={[{ required: true }]}>
                        <Input placeholder="Operator Id" />
                      </Form.Item>
                      <Button type="primary" htmlType="submit" loading={submittingWindow}>
                        Apply Service Window
                      </Button>
                    </Form>
                  </Card>

                  <Card title="Latest Result">
                    {windowResponse ? (
                      <Descriptions bordered size="small" column={1}>
                        <Descriptions.Item label="Context Id">{windowResponse.contextId}</Descriptions.Item>
                        <Descriptions.Item label="Applied">{String(windowResponse.applied)}</Descriptions.Item>
                        <Descriptions.Item label="Message">{windowResponse.message}</Descriptions.Item>
                        <Descriptions.Item label="Day">{dayLabel(windowResponse.serviceWindow.day)}</Descriptions.Item>
                        <Descriptions.Item label="Open At">{windowResponse.serviceWindow.openAt}</Descriptions.Item>
                        <Descriptions.Item label="Close At">{windowResponse.serviceWindow.closeAt}</Descriptions.Item>
                      </Descriptions>
                    ) : (
                      <Empty description="Submit a service window change to inspect the latest applied snapshot." />
                    )}
                  </Card>

                  <Card title="Recent Service Window Actions">
                    <Table<SetServiceWindowResponse>
                      pagination={false}
                      rowKey={(record) =>
                        `${record.contextId}-${record.message}-${record.serviceWindow.day}-${record.serviceWindow.openAt}`
                      }
                      dataSource={windowHistory}
                      locale={{ emptyText: 'No service window actions submitted yet.' }}
                      columns={[
                        { title: 'Context Id', dataIndex: 'contextId' },
                        { title: 'Day', render: (_, record) => dayLabel(record.serviceWindow.day) },
                        { title: 'Window', render: (_, record) => `${record.serviceWindow.openAt} - ${record.serviceWindow.closeAt}` },
                        {
                          title: 'Applied',
                          render: (_, record) => <Tag color={record.applied ? 'green' : 'red'}>{String(record.applied)}</Tag>
                        },
                        { title: 'Message', dataIndex: 'message' }
                      ]}
                    />
                  </Card>
                </Space>
              }
            />
            <Route
              path="/session"
              element={
                <Card title="Session Details">
                  <pre>{JSON.stringify(session.profile, null, 2)}</pre>
                </Card>
              }
            />
            <Route path="/" element={<Navigate to="/config" replace />} />
            <Route path="*" element={<Navigate to="/config" replace />} />
          </Routes>
        </Layout.Content>
      </Layout>
    </Layout>
  );
}

function dayLabel(day: number): string {
  return dayOptions.find((option) => option.value === day)?.label ?? String(day);
}

function formatUtc(value: string): string {
  return new Date(value).toLocaleString();
}

function providerLabel(provider: IdentityProvider): string {
  if (provider === 0 || provider === '0' || provider === 'InternalJwt') {
    return 'Internal JWT';
  }

  if (provider === 1 || provider === '1' || provider === 'AzureAd') {
    return 'Azure AD';
  }

  if (provider === 2 || provider === '2' || provider === 'Google') {
    return 'Google';
  }

  return String(provider);
}

export default App;
