import { Type, type Static } from "@sinclair/typebox";

export const DASHBOARD_SCHEMA_VERSION = "dashboard.v1" as const;

const IdentifierSchema = Type.String({
  minLength: 1,
  pattern: "^[a-z0-9][a-z0-9-_.:]*$",
});

const SeveritySchema = Type.Union([
  Type.Literal("neutral"),
  Type.Literal("ok"),
  Type.Literal("warning"),
  Type.Literal("danger"),
  Type.Literal("stale"),
  Type.Literal("unavailable"),
]);

const IconReferenceSchema = Type.String({ minLength: 1 });

const LinkSchema = Type.Object(
  {
    href: Type.String({ format: "uri" }),
    label: Type.Optional(Type.String({ minLength: 1 })),
    external: Type.Optional(Type.Boolean()),
  },
  { additionalProperties: false },
);

export const StaticDatasourceSchema = Type.Object(
  {
    type: Type.Literal("static"),
    label: Type.Optional(Type.String({ minLength: 1 })),
    updatedAt: Type.Optional(Type.String({ format: "date-time" })),
  },
  { additionalProperties: false },
);

export const PlaceholderDatasourceSchema = Type.Object(
  {
    type: Type.Literal("placeholder"),
    reason: Type.String({ minLength: 1 }),
  },
  { additionalProperties: false },
);

export const ExternalDatasourceSchema = Type.Object(
  {
    type: Type.Literal("external"),
    adapter: Type.Union([
      Type.Literal("prometheus"),
      Type.Literal("http-status"),
      Type.Literal("weather"),
      Type.Literal("custom"),
    ]),
    reference: Type.String({ minLength: 1 }),
  },
  { additionalProperties: false },
);

export const DatasourceReferenceSchema = Type.Union([
  StaticDatasourceSchema,
  PlaceholderDatasourceSchema,
  ExternalDatasourceSchema,
]);

const PercentMetricValueSchema = Type.Object(
  {
    kind: Type.Literal("percent"),
    value: Type.Number({ minimum: 0, maximum: 100 }),
    unit: Type.Optional(Type.String({ minLength: 1 })),
    precision: Type.Optional(Type.Integer({ minimum: 0, maximum: 4 })),
  },
  { additionalProperties: false },
);

const NonNegativeMetricValueSchema = Type.Object(
  {
    kind: Type.Union([
      Type.Literal("bytes"),
      Type.Literal("temperature"),
      Type.Literal("latency"),
      Type.Literal("number"),
    ]),
    value: Type.Number({ minimum: 0 }),
    unit: Type.Optional(Type.String({ minLength: 1 })),
    precision: Type.Optional(Type.Integer({ minimum: 0, maximum: 4 })),
  },
  { additionalProperties: false },
);

const TextMetricValueSchema = Type.Object(
  {
    kind: Type.Literal("text"),
    value: Type.String(),
    unit: Type.Optional(Type.String({ minLength: 1 })),
  },
  { additionalProperties: false },
);

export const MetricValueSchema = Type.Union([
  PercentMetricValueSchema,
  NonNegativeMetricValueSchema,
  TextMetricValueSchema,
]);

export const ThresholdSchema = Type.Object(
  {
    warning: Type.Optional(Type.Number({ minimum: 0 })),
    danger: Type.Optional(Type.Number({ minimum: 0 })),
  },
  { additionalProperties: false, minProperties: 1 },
);

export const TelemetryCardSchema = Type.Object(
  {
    id: IdentifierSchema,
    label: Type.String({ minLength: 1 }),
    description: Type.Optional(Type.String()),
    icon: Type.Optional(IconReferenceSchema),
    value: MetricValueSchema,
    detail: Type.Optional(Type.String()),
    severity: SeveritySchema,
    thresholds: Type.Optional(ThresholdSchema),
    datasource: Type.Optional(DatasourceReferenceSchema),
    sparkline: Type.Optional(Type.Array(Type.Number())),
  },
  { additionalProperties: false },
);

export const ServiceEntrySchema = Type.Object(
  {
    id: IdentifierSchema,
    label: Type.String({ minLength: 1 }),
    description: Type.String(),
    icon: Type.Optional(IconReferenceSchema),
    link: Type.Optional(LinkSchema),
    severity: SeveritySchema,
    detail: Type.Optional(Type.String()),
    datasource: Type.Optional(DatasourceReferenceSchema),
  },
  { additionalProperties: false },
);

export const ServiceGroupSchema = Type.Object(
  {
    id: IdentifierSchema,
    title: Type.String({ minLength: 1 }),
    layout: Type.Optional(Type.Union([Type.Literal("list"), Type.Literal("grid")])),
    services: Type.Array(ServiceEntrySchema),
  },
  { additionalProperties: false },
);

export const StatusItemSchema = Type.Object(
  {
    id: IdentifierSchema,
    label: Type.String({ minLength: 1 }),
    value: Type.String(),
    severity: Type.Optional(SeveritySchema),
    link: Type.Optional(LinkSchema),
  },
  { additionalProperties: false },
);

export const StatusStripSchema = Type.Object(
  {
    id: IdentifierSchema,
    title: Type.Optional(Type.String({ minLength: 1 })),
    items: Type.Array(StatusItemSchema),
  },
  { additionalProperties: false },
);

export const DashboardModuleSchema = Type.Object(
  {
    id: IdentifierSchema,
    kind: Type.Union([
      Type.Literal("summary"),
      Type.Literal("weather"),
      Type.Literal("custom"),
    ]),
    title: Type.Optional(Type.String({ minLength: 1 })),
    label: Type.Optional(Type.String()),
    value: Type.Optional(Type.String()),
    detail: Type.Optional(Type.String()),
    icon: Type.Optional(IconReferenceSchema),
    severity: Type.Optional(SeveritySchema),
    datasource: Type.Optional(DatasourceReferenceSchema),
  },
  { additionalProperties: false },
);

const LayoutSchema = Type.Object(
  {
    density: Type.Optional(Type.Union([Type.Literal("compact"), Type.Literal("dense")])),
    telemetry: Type.Array(IdentifierSchema),
    serviceGroups: Type.Array(IdentifierSchema),
    statusStrips: Type.Array(IdentifierSchema),
    modules: Type.Optional(Type.Array(IdentifierSchema)),
  },
  { additionalProperties: false },
);

const MetadataSchema = Type.Object(
  {
    title: Type.String({ minLength: 1 }),
    subtitle: Type.Optional(Type.String()),
    description: Type.Optional(Type.String()),
    timezone: Type.Optional(Type.String({ minLength: 1 })),
    refreshIntervalSeconds: Type.Optional(Type.Integer({ minimum: 5 })),
  },
  { additionalProperties: false },
);

export const DashboardDocumentSchema = Type.Object(
  {
    schemaVersion: Type.Literal(DASHBOARD_SCHEMA_VERSION),
    metadata: MetadataSchema,
    layout: LayoutSchema,
    telemetry: Type.Array(TelemetryCardSchema),
    serviceGroups: Type.Array(ServiceGroupSchema),
    statusStrips: Type.Array(StatusStripSchema),
    modules: Type.Optional(Type.Array(DashboardModuleSchema)),
    migration: Type.Optional(
      Type.Object(
        {
          previousVersion: Type.Optional(Type.String({ minLength: 1 })),
          notes: Type.Optional(Type.String()),
        },
        { additionalProperties: false },
      ),
    ),
  },
  {
    $id: "https://dimensionlab.net/schemas/dashboard-document.v1.json",
    additionalProperties: false,
  },
);

export type Severity = Static<typeof SeveritySchema>;
export type DatasourceReference = Static<typeof DatasourceReferenceSchema>;
export type MetricValue = Static<typeof MetricValueSchema>;
export type TelemetryCard = Static<typeof TelemetryCardSchema>;
export type ServiceEntry = Static<typeof ServiceEntrySchema>;
export type ServiceGroup = Static<typeof ServiceGroupSchema>;
export type StatusItem = Static<typeof StatusItemSchema>;
export type StatusStrip = Static<typeof StatusStripSchema>;
export type DashboardModule = Static<typeof DashboardModuleSchema>;
export type DashboardDocument = Static<typeof DashboardDocumentSchema>;

export const dashboardDocumentJsonSchema = DashboardDocumentSchema;
