import { Dimension } from './Dimension.js';
import { EventDefinitionLoose } from './event.js';
import { Margin } from './Margin.js';
import { Padding } from './Padding.js';
import { DynamicPath6, EntityWithBindings6 } from './shared.js';

export interface WidgetProps4and5 {
  type: string;
  widgetId: string;
  children?: WidgetProps4and5[];
  leftColumn?: number;
  rightColumn?: number;
  topRow: number;
  bottomRow: number;
  snapColumns?: number;
  snapRows?: number;
  minHeight?: number; // Required to reduce the size of CanvasWidgets.
  [key: string]: unknown;
}

// TODO: Replace with better types
interface WidgetRowCols6 {
  leftColumn?: number;
  rightColumn?: number;
  // @type integer
  topRow: number;
  // @type integer
  bottomRow: number;
  // @type integer
  snapColumns?: number;
  // @type integer
  snapRows?: number;
  minHeight?: number; // Required to reduce the size of CanvasWidgets.
}

export interface WidgetProps6 extends WidgetRowCols6, EntityWithBindings6 {
  type: string;
  widgetId: string;
  widgetName: string;
  parentId?: string;
  children?: WidgetProps6[];
  detachFromLayout?: boolean;
  [key: string]: unknown;

  noContainerOffset?: boolean;
  noPad?: boolean;
  containerStyle?: 'none' | 'card' | string;
}

type GridLayoutProps = {
  gridColumns: number;
};

export type WidgetPosition = {
  // Only for children of GridWidgets
  left: Dimension<'gridUnit'>;
  top: Dimension<'gridUnit'>;

  // Only for children of GridWidgets
  width: Dimension<'gridUnit' | 'fitContent' | 'fillParent' | 'px'>;
  height: Dimension<'gridUnit' | 'fitContent' | 'fillParent' | 'px'>;

  minHeight?: Dimension<'gridUnit' | 'px'>;
  maxHeight?: Dimension<'gridUnit' | 'px'>;

  minWidth?: Dimension<'px'>;
  maxWidth?: Dimension<'px'>;

  padding?: Padding;
  margin?: Margin;
} & Partial<GridLayoutProps>;

type WidgetVStackProperties = {
  spacing?: Dimension<'gridUnit' | 'px'>;
};

export type WidgetProps7 = EntityWithBindings6 &
  WidgetPosition &
  WidgetVStackProperties & {
    type: string;
    widgetId: string;
    widgetName: string;
    parentId?: string;
    children?: WidgetProps7[];
    [key: string]: unknown;
  };

export type WidgetProps8 = WidgetProps7;

export enum ApiResponseType {
  SYNC = 'SYNC',
  STREAM = 'STREAM'
}

export interface ApiInfo6 extends EntityWithBindings6 {
  id: string;
  onSuccess?: unknown[];
  onError?: unknown[];
  onCancel?: unknown[];
  onMessage?: unknown[];
  responseType?: ApiResponseType;
}

export interface PageDSL4and5 extends WidgetProps4and5 {
  version: 4 | 5;
}

export type AppTimer6 = {
  id: string;
  name: string;
  startOnPageLoad: boolean;
  intervalMs: number;
  steps: unknown[];
  createdAt: number;
  dynamicTriggerPathList?: DynamicPath6[];
};

// The values of this enum are both displayed in the UI and Persisted to the
// DSL. Be careful when making updates.
export enum AppStateVarPersistence6 {
  TEMPORARY = 'Temporary',
  LOCAL_STORAGE = 'Local Storage'
}

export type AppStateVar6 = {
  id: string;
  name: string;
  defaultValue: string;
  createdAt: number;
  persistance: AppStateVarPersistence6;
} & EntityWithBindings6;

export type CodeModeAppStateVar = {
  id: string;
  name: string;
  defaultValue: string;
  createdAt: number;
  persistence: AppStateVarPersistence6;
} & EntityWithBindings6;

export interface CachedData6 {
  // These are sometimes set
  apiNames?: string[];
  apiDeps?: Record<string, string[]>;
  pageLoadActions?: {
    apiNames: string[];
    apiDeps: Record<string, string[]>;
  };
}

export type EmbedProperty8 = {
  id: string;
  name: string;
  defaultValue: string;
  createdAt: number;
  onChange?: unknown[];
} & EntityWithBindings6;

export interface PageDSL6 extends WidgetProps6 {
  version: 6;
  timers?: {
    timerMap: Record<AppTimer6['id'], AppTimer6>;
  };
  stateVars?: {
    stateVarMap: Record<AppStateVar6['id'], AppStateVar6>;
  };
  apis?: {
    // apiMap: Record<ApiInfo6['id'], ApiInfo6>;
    apiMap?: {
      [key: string]: ApiInfo6;
    };
  };
  cachedData?: CachedData6;
}

export type PageDSL7 = WidgetProps7 & {
  version: 7;
  timers: {
    timerMap: Record<AppTimer6['id'], AppTimer6>;
  };
  stateVars: {
    stateVarMap: Record<AppStateVar6['id'], AppStateVar6>;
  };
  apis: {
    apiMap: Record<ApiInfo6['id'], ApiInfo6>;
  };
  cachedData?: CachedData6;
};

export type PageDSL8 = WidgetProps8 & {
  version: 8;
  timers: {
    timerMap: Record<AppTimer6['id'], AppTimer6>;
  };
  stateVars: {
    stateVarMap: Record<AppStateVar6['id'], AppStateVar6>;
  };
  apis: {
    apiMap: Record<ApiInfo6['id'], ApiInfo6>;
  };
  cachedData?: CachedData6;
  embedding?: {
    propertyMap: Record<EmbedProperty8['id'], EmbedProperty8>;
    triggerableEvents?: Record<string, boolean>;
    emittedEvents?: Record<string, boolean>;
  };

  minWidth?: Dimension<'px'>;
  maxWidth?: Dimension<'px'>;

  // This is a MultiStepDef but that type is not easy to validate yet since we do not
  // clear keys correctly in form based triggers
  onPageLoad?: unknown[];

  // This is a temporary field to store the layout version
  // @gpoulios-sb: Will remove this with a proper migration
  layoutVersion?: number;

  events?: {
    eventMap: Record<string, EventDefinitionLoose>;
  };
};

export type WidgetProps = WidgetProps8;
export type AnyPageDSL = PageDSL4and5 | PageDSL6 | PageDSL7 | PageDSL8;
export type PageDSLWithBackwardsCompatibility = AnyPageDSL;
export type PageDSL = PageDSL8;

export interface Layout<T = PageDSL> {
  id: string;
  dsl: T;
  updated: string;

  // FIXME: Unused properties
  actionUpdates: Record<string, unknown>[];
  layoutOnLoadActions: Record<string, unknown>[];
  messages: Record<string, unknown>[];
  userPermissions: Record<string, unknown>[];
}

export interface ILayoutDto {
  id: string;
  dsl: PageDSL;

  // FIXME: Unused properties
  actionUpdates: Record<string, unknown>[];
  layoutOnLoadActions: Record<string, unknown>[];
  messages: Record<string, unknown>[];
  userPermissions: Record<string, unknown>[];
  updated: Date;
}

export interface ILayout<T = PageDSL> {
  id: string;
  dsl: T;
}
