import {
  Disclosure,
  DisclosureContent,
  DisclosureProvider,
} from "@ariakit/react";
import { useAppConfig } from "../AppConfig/AppConfig.tsx";
import {
  useLastSuccessfulSyncTs,
  useSyncLog,
  useSyncState,
  type SyncState,
} from "../store/index.tsx";
import type { SyncAttempt } from "../store/types.ts";
import type { FailurePayload } from "../types.ts";
import { log } from "./style.css.ts";
import { SyncIcon } from "./SyncIcon.tsx";

function SyncActionSuccess(props: {
  heading: string;
  items: { id: string; name: string; deleted: boolean }[];
}) {
  const { heading, items } = props;

  return (
    <div>
      <div>
        <strong>{heading}</strong>
      </div>

      <div style={{ marginBlockStart: "0.25rem" }}>
        {items.length > 0 ?
          <ul
            style={{
              listStyle: "initial",
              paddingInlineStart: "1.5rem",
            }}
          >
            {items.map((item) => {
              const name = item.name || "Unnamed item";

              if (item.deleted) {
                return (
                  <li key={item.id}>
                    Synced deletion of {name} (<code>{item.id}</code>)
                  </li>
                );
              }

              return (
                <li key={item.id}>
                  Synced {name} (<code>{item.id}</code>)
                </li>
              );
            })}
          </ul>
        : <div style={{ opacity: 0.5 }}>
            <em>Everything up to date.</em>
          </div>
        }
      </div>
    </div>
  );
}

const failureToLabel: Record<FailurePayload["type"], string> = {
  NETWORK_ERROR: "A network error occurred.",
  API_ERROR: "Server responded with error ",
  UNKNOWN_ERROR: "An unexpected error ocurred. Try restarting the app.",
  VALIDATION_ERROR: "Data validation error occurred. Try restarting the app.",
};

function SyncActionFailure(props: {
  heading: string;
  failure: FailurePayload;
}) {
  const { heading, failure } = props;
  const label = failureToLabel[failure.type];
  return (
    <div>
      {heading}
      {": "}
      {failure.type === "API_ERROR" ?
        ` ${label} ${failure.code}.`
      : ` ${label}`}
    </div>
  );
}

const syncStateToLabel: Record<SyncState, string> = {
  WAITING: "Waiting for additional changes...",
  IDLE: "Sync enabled. Waiting for changes.",
  AUTHENTICATING: "Verifying identity...",
  ERROR: "Last sync unsuccessful. Waiting for changes.",
  INACTIVE: "Sync not enabled. Log in or join to enable.",
  SYNCING: "Syncing...",
};

export function SyncLogList(props: {
  syncState: SyncState;
  syncLog: SyncAttempt[];
  lastSuccessfulSyncTs: number | null;
}) {
  const { syncLog, syncState, lastSuccessfulSyncTs } = props;
  const { fmt } = useAppConfig();

  return (
    <div>
      <div className={log.item}>
        <SyncIcon status={syncState} />

        <div>
          <div>{syncStateToLabel[syncState]}</div>

          <div className={log.itemTimestamp}>
            {lastSuccessfulSyncTs ?
              `Last successful sync at: ${fmt.dateTime(lastSuccessfulSyncTs)}`
            : "Not synced yet."}
          </div>
        </div>
      </div>

      {syncLog.map((logItem) => {
        const isFailure =
          logItem.pull?.type === "FAILURE" || logItem.push?.type == "FAILURE";

        const itemsSynced =
          (logItem.pull?.type === "SUCCESS" ?
            logItem.pull.value.pulled.length
          : 0) +
          (logItem.push?.type === "SUCCESS" ?
            logItem.push.value.pushed.length + logItem.push.value.pulled.length
          : 0);

        return (
          <DisclosureProvider key={logItem.startedTs}>
            <Disclosure className={log.item}>
              {isFailure ?
                <>
                  <SyncIcon status="ERROR" />
                  <div>
                    Sync attempt failed
                    <div className={log.itemTimestamp}>
                      {fmt.dateTime(logItem.startedTs)}
                    </div>
                  </div>
                </>
              : <>
                  <SyncIcon status="IDLE" />
                  <div>
                    {"Sync successful. "}
                    {itemsSynced === 0 ?
                      "Everything up to date."
                    : fmt.plural(itemsSynced, {
                        one: "1 item synced.",
                        other: "# items synced.",
                      })
                    }
                    <div className={log.itemTimestamp}>
                      {fmt.dateTime(logItem.startedTs)}
                    </div>
                  </div>
                </>
              }
            </Disclosure>

            <DisclosureContent className={log.disclosureContent}>
              {logItem.pull && (
                <>
                  {logItem.pull.type === "SUCCESS" ?
                    <SyncActionSuccess
                      heading="Pull successful"
                      items={logItem.pull.value.pulled}
                    />
                  : <SyncActionFailure
                      heading="Pull failed"
                      failure={logItem.pull.failure}
                    />
                  }
                </>
              )}

              {logItem.push && (
                <>
                  {logItem.push.type === "SUCCESS" ?
                    <>
                      <SyncActionSuccess
                        heading="Push successful"
                        items={logItem.push.value.pushed}
                      />
                      <SyncActionSuccess
                        heading="Patch successful"
                        items={logItem.push.value.pulled}
                      />
                    </>
                  : <SyncActionFailure
                      heading="Push failed"
                      failure={logItem.push.failure}
                    />
                  }
                </>
              )}
            </DisclosureContent>
          </DisclosureProvider>
        );
      })}
    </div>
  );
}

export function SyncLog() {
  const syncState = useSyncState();
  const syncLog = useSyncLog();
  const lastSuccessfulSyncTs = useLastSuccessfulSyncTs();

  return (
    <SyncLogList
      syncState={syncState}
      syncLog={syncLog}
      lastSuccessfulSyncTs={lastSuccessfulSyncTs}
    />
  );
}
