import { faker } from '@faker-js/faker';
import { action } from '@storybook/addon-actions';
import { Meta, StoryObj } from '@storybook/react';
import { Field } from 'formik';
import React from 'react';
import { MemoryRouter } from 'react-router';
import { Link } from 'react-router-dom';
import * as Yup from 'yup';
import { createGroups, generateItemArray } from '../../helpers/storybook';
import { ActionData } from '../Actions';
import {
  createInputRenderer,
  createSelectRenderer,
} from '../DynamicDataList/DynamicListDataEntry/Renderers';
import {
  CustomTagsField,
  DateTimeTextField,
  FormGroup,
  ReadOnlyField,
  SelectField,
  SingleLineTextField,
  TagsField,
} from '../FormElements';
import { CheckboxField } from '../FormElements/Checkbox/CheckboxField';
import { DynamicDataListField } from '../FormElements/DynamicDataListControl/DynamicDataListField';
import { MaskedSingleLineTextField } from '../FormElements/MaskedSingleLineText/MaskedSingleLineTextField';
import { InfoPanel, Paragraph, Section } from '../InfoPanel';
import { Tab, TabList, TabPanel, Tabs } from '../Tabs';
import { ErrorType } from '../models';
import { Details, DetailsProps } from './Details/Details';
import { FormGrid } from './FormGrid';
import { ObjectSchemaDefinition } from './FormStation.models';

const groups = createGroups({
  Content: [
    'initialData',
    'validationSchema',
    'children',
    'actions',
    'defaultTitle',
    'titleProperty',
    'subtitle',
    'infoPanel',
    'stationMessage',
  ],
  Behavior: [
    'saveData',
    'alwaysShowActionsPanel',
    'alwaysSubmitBeforeAction',
    'edgeToEdgeContent',
    'setTabTitle',
  ],
  Styling: ['actionsWidth', 'className'],
});

const meta: Meta<typeof Details> = {
  title: 'Primary Components/Station Controls/FormStation/Details',
  component: Details,
  decorators: [
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
    (Story) => (
      <MemoryRouter>
        <Story />
      </MemoryRouter>
    ),
  ],
  argTypes: {
    ...groups,
  },
};
export default meta;

interface APIResponse {
  status: string;
}

interface DynamicListStoryData {
  position: number;
  id: number;
  locale: string;
  country: string;
}

const generateActions = (amount: number): ActionData[] =>
  generateItemArray(
    amount,
    (index) =>
      ({
        label: `Action ${index + 1}`,
        onActionSelected: action(`onActionSelected (Action ${index + 1})`),
      } as ActionData),
  );

const defaultActions: ActionData[] = generateActions(3);

export const Default: StoryObj<typeof Details> = {
  args: {
    defaultTitle: 'Details Station',
    titleProperty: 'title',
    subtitle: 'My Asset Type',
    actions: defaultActions,
    validationSchema: Yup.object<ObjectSchemaDefinition<{ title: string }>>({
      title: Yup.string().required().max(25).label('Title'),
    }),
    initialData: { data: { title: 'My asset' }, loading: false },
    children: (
      <div>
        <h1 style={{ textAlign: 'center', marginTop: 0 }}>
          Define the form as you like ✨
        </h1>
        <Field name="title" label="Title" as={SingleLineTextField} />
      </div>
    ),
    saveData: (...args: unknown[]) => {
      action('saveData')(args);
      return new Promise<APIResponse>((resolve) => {
        setTimeout(() => {
          resolve({ status: 'ok' });
        }, 500);
      });
    },
  },
};
interface DetailsValues {
  id?: number;
  title?: string;
  publishState?: string;
  genres?: string[];
  licenses?: string[];
  ratings?: string;
  shortDescription?: string;
  longDescription?: string;
  cast?: string[];
  released?: string;
  list?: DynamicListStoryData[];
  archived?: boolean;
  timestamp?: string;
}

export const Extended: StoryObj<typeof Details> = (() => {
  const listData = generateItemArray(4, (index) => ({
    position: index + 1,
    id: index,
    locale: faker.random.locale(),
    country: faker.address.country(),
  }));
  return {
    args: {
      titleProperty: 'title',
      subtitle: 'Movies',
      actions: generateActions(7),
      validationSchema: Yup.object<ObjectSchemaDefinition<DetailsValues>>({
        title: Yup.string().required().max(25).label('Title'),
        genres: Yup.array<DetailsValues>()
          .of(Yup.string())
          .max(2)
          .label('Genres'),
        shortDescription: Yup.string().required().label('Short Description'),
        cast: Yup.array<DetailsValues>().of(Yup.string()).min(1).label('Cast'),
      }),
      initialData: {
        loading: false,
        data: {
          id: 12344567890,
          title: 'My Movie',
          publishState: 'PUBLISHED',
          genres: [],
          licenses: [],
          ratings: 'PG-13',
          shortDescription: 'Some short abstract...',
          longDescription: '',
          cast: ['Jane Doe', 'John Doe'],
          released: '2020-04-03',
          list: listData,
          archived: false,
          timestamp: '00:00:00.001',
        },
      },
      infoPanel: (
        <InfoPanel>
          <Section title="Additional Information">
            <Paragraph title="ID">ID VALUE</Paragraph>
            <Paragraph title="Transcoding Job ID">JOB ID VALUE</Paragraph>
            <Paragraph title="Source File Name">VIDEOSOURCE</Paragraph>
            <Paragraph title="Source Location">PATH</Paragraph>
            <Paragraph title="Source File Metadata">METADATA</Paragraph>
            <Paragraph title="Finished At">FINISHED DATE</Paragraph>
          </Section>
        </InfoPanel>
      ),
      children: (
        <>
          <Field name="id" label="Id" as={ReadOnlyField} />
          <Field name="title" label="Title" as={SingleLineTextField} />
          <Field name="publishState" label="Publish State" as={ReadOnlyField} />
          <Field
            name="genres"
            label="Genre(s)"
            tagsOptions={['Crime', 'Drama', 'Thriller']}
            as={TagsField}
          />

          <Field
            name="ratings"
            label="Age Rating"
            options={[
              { value: 'PG', label: 'Parental Guidance Suggested (PG)' },
              { value: 'PG-13', label: 'Parents Strongly Cautioned (PG-13)' },
              { value: 'R', label: 'Restricted (R)' },
            ]}
            as={SelectField}
          />
          <FormGroup title="Collapsible Group" isCollapsible={true}>
            <Field
              name="shortDescription"
              label="Short Description"
              placeholder="Enter a short description..."
              as={SingleLineTextField}
            />
            <Field
              name="longDescription"
              label="Long Description"
              placeholder="Enter a description..."
              as={SingleLineTextField}
            />
          </FormGroup>
          <Field name="cast" label="Cast" as={CustomTagsField} />
          <Field
            name="released"
            label="Released"
            as={DateTimeTextField}
            modifyTime={false}
          />
          <Field
            name="password"
            label="Password"
            as={SingleLineTextField}
            type="password"
          />

          <Field
            name="licenses"
            label="License Countries"
            tagsOptions={[
              { value: 'DE', key: 'Germany' },
              { value: 'EE', key: 'Estonia' },
              { value: 'LK', key: 'Sri Lanka' },
            ]}
            as={TagsField}
            displayKey="key"
            valueKey="value"
          />
          <Field
            name="list"
            label="Subtitles"
            columns={[
              {
                propertyName: 'id',
                label: 'Id',
                size: '50px',
              },
              {
                propertyName: 'locale',
                label: 'Locale',
                dataEntryRender: createInputRenderer({
                  placeholder: 'Enter Locale',
                }),
              },
              {
                propertyName: 'country',
                label: 'Country',
                dataEntryRender: createSelectRenderer({
                  options: [
                    { value: 'Country 10', label: 'Country 10' },
                    { value: 'Country 11', label: 'Country 11' },
                    { value: 'Country 12', label: 'Country 12' },
                  ],
                  placeholder: 'Enter Country',
                }),
              },
            ]}
            data={listData}
            positionPropertyName={'position'}
            allowReordering={true}
            allowRowDragging={true}
            allowNewData={true}
            as={DynamicDataListField}
          />
          <Field name="archived" label="Set Archived" as={CheckboxField} />
          <Field
            name="timestamp"
            label="Timestamp"
            mask="00:00:00.000"
            as={MaskedSingleLineTextField}
          />
        </>
      ),
    },
  };
})();

export const TabbedContent: StoryObj<typeof Details> = (() => {
  const listData = generateItemArray(4, (index) => ({
    position: index + 1,
    id: index,
    locale: faker.random.locale(),
    country: faker.address.country(),
  }));
  return {
    args: {
      titleProperty: 'title',
      subtitle: 'Movies',
      actions: generateActions(7),
      validationSchema: Yup.object<ObjectSchemaDefinition<DetailsValues>>({
        title: Yup.string().required().max(25).label('Title'),
        genres: Yup.array<DetailsValues>()
          .of(Yup.string())
          .max(2)
          .label('Genres'),
        shortDescription: Yup.string().required().label('Short Description'),
        cast: Yup.array<DetailsValues>().of(Yup.string()).min(1).label('Cast'),
      }),
      edgeToEdgeContent: true,
      initialData: {
        loading: false,
        data: {
          id: 12344567890,
          title: 'My Movie',
          publishState: 'PUBLISHED',
          genres: [],
          licenses: [],
          ratings: 'PG-13',
          shortDescription: 'Some short abstract...',
          longDescription: '',
          cast: ['Jane Doe', 'John Doe'],
          released: '2020-04-03T00:00:00.000+00:00',
          list: listData,
          archived: false,
          timestamp: '00:00:00.001',
        },
      },
      children: (
        <Tabs>
          <TabList>
            <Tab>Tab 1</Tab>
            <Tab>Tab 2</Tab>
          </TabList>
          <TabPanel>
            <FormGrid>
              <Field name="id" label="Id" as={ReadOnlyField} />
              <Field name="title" label="Title" as={SingleLineTextField} />
              <Field
                name="publishState"
                label="Publish State"
                as={ReadOnlyField}
              />
              <Field
                name="genres"
                label="Genre(s)"
                tagsOptions={['Crime', 'Drama', 'Thriller']}
                as={TagsField}
              />

              <Field
                name="ratings"
                label="Age Rating"
                options={[
                  { value: 'PG', label: 'Parental Guidance Suggested (PG)' },
                  {
                    value: 'PG-13',
                    label: 'Parents Strongly Cautioned (PG-13)',
                  },
                  { value: 'R', label: 'Restricted (R)' },
                ]}
                as={SelectField}
              />
              <Tabs>
                <TabList>
                  <Tab>Nested Tab 1</Tab>
                  <Tab>Nested Tab 2</Tab>
                </TabList>
                <TabPanel>
                  <FormGrid>
                    <Field
                      name="shortDescription"
                      label="Short Description"
                      placeholder="Enter a short description..."
                      as={SingleLineTextField}
                    />
                    <Field
                      name="longDescription"
                      label="Long Description"
                      placeholder="Enter a description..."
                      as={SingleLineTextField}
                    />
                    <Field name="cast" label="Cast" as={CustomTagsField} />
                    <Field
                      name="released"
                      label="Released"
                      as={DateTimeTextField}
                    />
                    <Field
                      name="password"
                      label="Password"
                      as={SingleLineTextField}
                      type="password"
                    />
                  </FormGrid>
                </TabPanel>
                <TabPanel>
                  <FormGrid>
                    <Field
                      name="licenses"
                      label="License Countries"
                      tagsOptions={[
                        { value: 'DE', key: 'Germany' },
                        { value: 'EE', key: 'Estonia' },
                        { value: 'LK', key: 'Sri Lanka' },
                      ]}
                      as={TagsField}
                      displayKey="key"
                      valueKey="value"
                    />
                    <Field
                      name="list"
                      label="Subtitles"
                      columns={[
                        {
                          propertyName: 'id',
                          label: 'Id',
                          size: '50px',
                        },
                        {
                          propertyName: 'locale',
                          label: 'Locale',
                          dataEntryRender: createInputRenderer({
                            placeholder: 'Enter Locale',
                          }),
                        },
                        {
                          propertyName: 'country',
                          label: 'Country',
                          dataEntryRender: createSelectRenderer({
                            options: [
                              { value: 'Country 10', label: 'Country 10' },
                              { value: 'Country 11', label: 'Country 11' },
                              { value: 'Country 12', label: 'Country 12' },
                            ],
                            placeholder: 'Enter Country',
                          }),
                        },
                      ]}
                      data={listData}
                      positionPropertyName={'position'}
                      allowReordering={true}
                      allowRowDragging={true}
                      allowNewData={true}
                      as={DynamicDataListField}
                    />
                    <Field
                      name="archived"
                      label="Set Archived"
                      as={CheckboxField}
                    />
                    <Field
                      name="timestamp"
                      label="Timestamp"
                      mask="00:00:00.000"
                      as={MaskedSingleLineTextField}
                    />
                  </FormGrid>
                </TabPanel>
              </Tabs>
            </FormGrid>
          </TabPanel>
          <TabPanel>
            <h2>Tab 2</h2>
            <p>{faker.lorem.paragraph(20)}</p>
          </TabPanel>
        </Tabs>
      ),
    },
  };
})();

const errorGroups = createGroups({
  'Storybook (Loading Error)': [
    'loadingError',
    'customLoadingError',
    'entityNotFound',
  ],
  'Storybook (Saving Error)': ['savingError', 'customSavingError'],
  'Storybook (Action Error)': ['actionError', 'customActionError'],
});

export const StationError: StoryObj<
  DetailsProps<{ title: string }> & {
    entityNotFound: boolean;
    loadingError: 'none' | 'string' | 'Object' | 'StationError' | 'custom';
    customLoadingError: ErrorType;
    savingError: 'none' | 'string' | 'Object' | 'StationError' | 'custom';
    customSavingError: ErrorType;
    actionError: 'none' | 'string' | 'Object' | 'StationError' | 'custom';
    customActionError: ErrorType;
  }
> = {
  parameters: {
    docs: {
      description: {
        story:
          'An Error thrown on the station will render a MessageBar with the title and an optional body',
      },
    },
  },
  argTypes: {
    ...errorGroups,
    loadingError: {
      ...errorGroups.loadingError,
      description:
        '<b>[Storybook only]</b> Use this to test the different types of errors that could happen during loading.',
      control: {
        type: 'select',
        labels: {
          custom: "Custom (define error in 'customLoadingError')",
        },
      },
      options: ['undefined', 'string', 'Object', 'StationError', 'custom'],
      mapping: {
        undefined: undefined,
        string: 'My error string',
        StationError: { title: 'My custom title', body: 'My custom body' },
        Object: { property: 'My custom property' },
      },
      defaultValue: 'none',
    },
    customLoadingError: {
      ...errorGroups.customLoadingError,
      control: 'object',
      description:
        "<b>[Storybook only]</b> Specify the value of the 'loadingErrorType.custom' here.",
    },
    entityNotFound: {
      ...errorGroups.entityNotFound,
      control: 'boolean',
      description: '<b>[Storybook only]</b> Simulate a 404 error.',
    },
    savingError: {
      ...errorGroups.savingError,
      description:
        '<b>[Storybook only]</b> Use this to test the different types of errors that could happen during saving.',
      control: {
        type: 'select',
        labels: {
          custom: "Custom (define error in 'customSavingError')",
        },
      },
      options: ['undefined', 'string', 'Object', 'StationError', 'custom'],
      mapping: {
        undefined: undefined,
        string: 'My error string',
        StationError: { title: 'My custom title', body: 'My custom body' },
        Object: { property: 'My custom property' },
      },
      defaultValue: 'none',
    },
    customSavingError: {
      ...errorGroups.customSavingError,
      control: 'object',
      description:
        "<b>[Storybook only]</b> Specify the value of the 'savingError.custom' here.",
    },
    actionError: {
      ...errorGroups.actionError,
      description:
        '<b>[Storybook only]</b> Use this to test the different types of errors that could happen during execution of an action.',
      control: {
        type: 'select',
        labels: {
          custom: "Custom (define error in 'customActionError')",
        },
      },
      options: ['undefined', 'string', 'Object', 'StationError', 'custom'],
      mapping: {
        undefined: undefined,
        string: 'My error string',
        StationError: { title: 'My custom title', body: 'My custom body' },
        Object: { property: 'My custom property' },
      },
      defaultValue: 'none',
    },
    customActionError: {
      ...errorGroups.customActionError,
      control: 'object',
      description:
        "<b>[Storybook only]</b> Specify the value of the 'actionError.custom' here.",
    },
  },
  args: {
    titleProperty: 'title',
    subtitle:
      "Edit the 'Title' field and click 'Execute Action' or try to use the link above to navigate.",
    validationSchema: Yup.object<ObjectSchemaDefinition<{ title: string }>>({
      title: Yup.string().required().max(25).label('Title'),
    }),
  },
  render: (args) => {
    const errorAction: ActionData[] = [
      {
        label: 'Execute Action',
        onActionSelected: () => {
          if (!args.actionError) {
            return;
          } else {
            return args.actionError === 'custom'
              ? args.customActionError
              : args.actionError;
          }
        },
      },
    ];

    const onSubmit = (...submitArgs: unknown[]): Promise<APIResponse> => {
      action('saveData')(submitArgs);
      return new Promise<APIResponse>((resolve, reject) => {
        if (args.savingError !== undefined) {
          reject(
            args.savingError === 'custom'
              ? args.customSavingError
              : args.savingError,
          );
        } else {
          resolve(undefined as unknown as APIResponse);
        }
      });
    };

    return (
      <>
        <p>
          To simulate a navigation, to trigger form saving, you can click{' '}
          <Link to="/">here</Link>
        </p>

        <Details<{ title: string }, APIResponse>
          {...args}
          actions={errorAction}
          initialData={{
            data: { title: 'My asset' },
            loading: false,
            error:
              args.loadingError === 'custom'
                ? args.customLoadingError
                : args.loadingError,
            entityNotFound: args.entityNotFound,
          }}
          saveData={onSubmit}
        >
          <div>
            <Field name="title" label="Title" as={SingleLineTextField} />
          </div>
        </Details>
      </>
    );
  },
};
