/* eslint-disable @typescript-eslint/no-unused-vars */
import { Endpoints } from '../v1';

type ReplacePlaceholders<TPath extends string> = string extends TPath
	? TPath
	: TPath extends `${infer Start}:${infer _Param}/${infer Rest}`
	? `${Start}${string}/${ReplacePlaceholders<Rest>}`
	: TPath extends `${infer Start}:${infer _Param}`
	? `${Start}${string}`
	: TPath;

type KeyOfEach<T> = T extends any ? keyof T : never;

type GetParams<TOperation> = TOperation extends (...args: any) => any
	? Parameters<TOperation>[0] extends void
		? void
		: Parameters<TOperation>[0]
	: never;

type GetResult<TOperation> = TOperation extends (...args: any) => any ? ReturnType<TOperation> : never;

type OperationsByPathPatternAndMethod<
	TPathPattern extends keyof Endpoints,
	TMethod extends KeyOfEach<Endpoints[TPathPattern]> = KeyOfEach<Endpoints[TPathPattern]>
> = TMethod extends any
	? {
			pathPattern: TPathPattern;
			method: TMethod;
			path: ReplacePlaceholders<TPathPattern>;
			params: GetParams<Endpoints[TPathPattern][TMethod]>;
			result: GetResult<Endpoints[TPathPattern][TMethod]>;
	  }
	: never;

type OperationsByPathPattern<TPathPattern extends keyof Endpoints> = TPathPattern extends any
	? OperationsByPathPatternAndMethod<TPathPattern>
	: never;

type Operations = OperationsByPathPattern<keyof Endpoints>;

type Method = Operations['method'];

export type PathPattern = Operations['pathPattern'];

type Path = Operations['path'];

//

export type Serialized<T> = T extends Date
	? Exclude<T, Date> | string
	: T extends boolean | number | string | null | undefined
	? T
	: T extends {}
	? {
			[K in keyof T]: Serialized<T[K]>;
	  }
	: null;

export type MatchPathPattern<TPath extends Path> = TPath extends any
	? Extract<Operations, { path: TPath }>['pathPattern']
	: never;

export type OperationResult<
	TMethod extends Method,
	TPathPattern extends PathPattern
> = TMethod extends keyof Endpoints[TPathPattern] ? GetResult<Endpoints[TPathPattern][TMethod]> : never;

export type PathFor<TMethod extends Method> = TMethod extends any ? Extract<Operations, { method: TMethod }>['path'] : never;

export type OperationParams<
	TMethod extends Method,
	TPathPattern extends PathPattern
> = TMethod extends keyof Endpoints[TPathPattern] ? GetParams<Endpoints[TPathPattern][TMethod]> : never;

type SuccessResult<T> = T & { success: true };

type FailureResult<T, TStack = undefined, TErrorType = undefined, TErrorDetails = undefined> = {
	success: false;
	error: T;
	stack: TStack;
	errorType: TErrorType;
	details: TErrorDetails;
};

type UnauthorizedResult<T> = {
	success: false;
	error: T | 'unauthorized';
};

export type ResultFor<TMethod extends Method, TPathPattern extends PathPattern> =
	| SuccessResult<OperationResult<TMethod, TPathPattern>>
	| FailureResult<unknown, unknown, unknown, unknown>
	| UnauthorizedResult<unknown>;

export type ErrorResult = FailureResult<unknown, unknown, unknown, unknown>;