/**
 * @vitest-environment happy-dom
 */
import { snap, waitFor } from '@factor/api'
import { getRandomWordsId } from '@factor/api/utils/words'
import type { TestUtils } from '@factor/api/test-utils/init'
import { createTestUtils } from '@factor/api/test-utils/init'
import { beforeAll, describe, expect, it } from 'vitest'
import { FactorAdmin } from '@factor/plugin-admin'
import { FactorSites } from '..'
import type { TableSiteConfig } from '../tables'
import { Site } from '../site'
import { theme } from '../themes/standard'

let site: TableSiteConfig
let siteObj: Site
let organizationId: string
let userId: string
let testUtils: undefined | TestUtils & { factorAdmin?: FactorAdmin, factorSites?: FactorSites }
let defaultNumRegions: number
describe('site plugin tests', () => {
  beforeAll(async () => {
    testUtils = await createTestUtils()
    testUtils.factorAdmin = new FactorAdmin(testUtils)
    testUtils.factorSites = new FactorSites({ ...testUtils, factorAdmin: testUtils.factorAdmin })

    const r = await testUtils.init()
    userId = r?.user?.userId ?? ''
    organizationId = r?.user?.organizations?.[0]?.organizationId ?? ''

    defaultNumRegions = theme.regions.value.filter(_ => _.regionKey === 'page').length
  })

  it('creates site', async () => {
    if (!testUtils?.factorSites)
      throw new Error('missing testUtils')

    const slug = getRandomWordsId()
    const title = 'test'
    const themeId = 'standard'
    const r = await testUtils?.factorSites?.queries.ManageSite.serve(
      {
        _action: 'create',
        fields: { title, themeId, slug },
        userId,
        organizationId,
      },
      { server: true, caller: 'testSiteCreation' },
    )

    if (!r?.data)
      throw new Error('problem creating site')

    site = r.data
    siteObj = new Site({ ...site, factorSites: testUtils?.factorSites })
    expect(r.status).toMatchInlineSnapshot(`"success"`)
    expect(r.message).toMatchInlineSnapshot(`"site created"`)
    expect(site).toBeTruthy()
    expect(site.slug).toBe(slug)
    expect(site.title).toBe(title)
    expect(site.themeId).toBe(themeId)
    expect(site.regions.filter(_ => _.regionKey === 'page').length).toBe(defaultNumRegions)
    expect(site.regions.filter(_ => _.regionKey === 'page').length).toMatchInlineSnapshot(`2`)
  })

  it('updates site', async () => {
    const title = 'testUpdate'

    if (!site?.siteId)
      throw new Error('siteId missing')

    const r = await testUtils?.factorSites?.queries.ManageSite.serve(
      {
        _action: 'update',
        fields: { title, siteId: site?.siteId },
        userId,
        organizationId,
      },
      { server: true },
    )

    if (!r?.data)
      throw new Error('problem updating site')

    expect(r?.status).toMatchInlineSnapshot(`"success"`)
    expect(r?.message).toMatchInlineSnapshot(`"site saved"`)

    site = r?.data

    expect(site).toBeTruthy()
    expect(site?.title).toBe(title)
    expect(site.regions.filter(_ => _.regionKey === 'page').length).toBe(defaultNumRegions)
    expect(site.regions.filter(_ => _.regionKey === 'page').length).toMatchInlineSnapshot(`2`)
  })

  it('sets defaults', async () => {
    if (!siteObj || !testUtils?.factorSites)
      throw new Error('missing siteObj or testUtils')

    expect(siteObj.activePageId.value, 'sets page id').toBeTruthy()
    expect(siteObj.obj.pages.value.some(p => p.cardId === siteObj.activePageId.value)).toBeTruthy()
  })

  it('creates region', async () => {
    if (!site?.siteId)
      throw new Error('siteId missing')

    const title = 'test'
    const slug = 'test'
    const r = await testUtils?.factorSites?.queries.ManageRegion.serve(
      {
        _action: 'upsert',
        fields: { siteId: site.siteId, title, slug },
        userId,
        organizationId,
      },
      { server: true, caller: 'testRegionCreation' },
    )

    if (!r?.data)
      throw new Error('problem creating region')

    const region = r.data
    expect(region).toBeTruthy()

    expect(region.title).toBe(title)
    expect(region.slug).toBe(slug)
    expect(region.siteId).toBe(site.siteId)

    expect(snap(region, { maskedKeys: [] })).toMatchInlineSnapshot(`
      {
        "cardId": "[id:****************************]",
        "cards": [],
        "createdAt": "[dateTime:]",
        "editor": {},
        "organizationId": "[id:**************************]",
        "regionKey": "page",
        "siteId": "[id:************************]",
        "slug": "test",
        "templateKey": "region",
        "title": "test",
        "updatedAt": "[dateTime:]",
        "userConfig": {},
        "userId": "[id:**************************]",
      }
    `)

    const r2 = await testUtils?.factorSites?.queries.ManageRegion.serve(
      {
        _action: 'delete',
        fields: { siteId: site?.siteId, cardId: region.cardId },
        userId,
        organizationId,
      },
      { server: true },
    )

    expect(r2?.status).toBe('success')
  })

  it('updates regions from form', async () => {
    if (!siteObj || !testUtils?.factorSites)
      throw new Error('missing siteObj or testUtils')

    expect(site.regions.length).toMatchInlineSnapshot(`2`)
    expect(siteObj.regions.value.length).toMatchInlineSnapshot(`4`)

    const rSite1 = await testUtils.factorSites.requests.ManageSite.projectRequest({
      _action: 'retrieve',
      fields: { siteId: siteObj.siteId },
    })

    expect(rSite1?.data?.regions.length).toMatchInlineSnapshot(`2`)

    const nm = 'test'
    const regionCard = { title: nm, slug: nm }

    const r = await siteObj.requestManageRegion({ _action: 'upsert', regionCard, delay: 0 })

    expect(r?.title).toBe(nm)
    expect(r?.cardId).toBeTruthy()

    expect(siteObj.regions.value.map(_ => _.regionKey)).toMatchInlineSnapshot(`
      [
        "page",
        "page",
        "page",
        "header",
        "footer",
      ]
    `)

    await waitFor(200)

    expect(siteObj.regions.value.filter(_ => _.regionKey === 'page').map(m => m.templateKey.value)).toMatchInlineSnapshot(`
      [
        "region",
        "engine",
        "engine",
      ]
    `)

    expect(siteObj.obj.pages.value.map(_ => _.regionKey)).toMatchInlineSnapshot(`
      [
        "page",
        "page",
        "page",
      ]
    `)

    expect(siteObj.regions.value.some(_ => !_.regionKey), 'region key always set').toBeFalsy()

    await waitFor(200)

    expect(siteObj.obj.pages.value.length).toMatchInlineSnapshot(`3`)

    expect(siteObj.regions.value[0].title.value).toBe(nm)

    expect(siteObj.regions.value.length, 'pages + 2 = all regions').toEqual(siteObj.obj.pages.value.length + 2)
    expect(siteObj.regions.value[0].cardId).toBe(r?.cardId)

    const rSite2 = await testUtils.factorSites.requests.ManageSite.projectRequest({
      _action: 'retrieve',
      fields: { siteId: siteObj.siteId },
    })

    expect(rSite2?.data?.regions.filter(_ => _.regionKey === 'page').length, 'default regions + 1 added page').toBe(defaultNumRegions + 1)

    await siteObj.requestManageRegion({ _action: 'delete', regionCard: { cardId: r?.cardId }, delay: 0 })

    await waitFor(200)

    expect(siteObj.regions.value.filter(_ => _.regionKey === 'page').length, 'default regions after adding and deleting page').toBe(defaultNumRegions)

    const rSite3 = await testUtils.factorSites.requests.ManageSite.projectRequest({
      _action: 'retrieve',
      fields: { siteId: siteObj.siteId },
    })

    expect(rSite3?.data?.regions.filter(_ => _.regionKey === 'page').length).toBe(defaultNumRegions)
  })

  it('handles active page', async () => {
    if (!siteObj || !testUtils?.factorSites)
      throw new Error('missing siteObj or testUtils')

    expect(siteObj.layout.page.value?.settings.slug).toMatchInlineSnapshot(`"test"`)
    expect(siteObj.activePageId.value).toBeTruthy()

    const nm = 'test'
    const regionCard = { title: nm, slug: nm }

    const created = await siteObj.requestManageRegion({ _action: 'upsert', regionCard, delay: 0 })

    await waitFor(200)

    expect(siteObj.activePageId.value).toBeTruthy()
    expect(created?.cardId).toBeTruthy()

    expect(siteObj.activePageId.value, 'created page to be active').toBe(created?.cardId)

    const cards = siteObj.layout.page.value?.cards

    const testId = 'testId_77'
    const testId2 = 'testId_88'

    const cardNumInitial = cards?.value?.length ?? 0

    expect(snap(siteObj.layout.page.value?.toConfig())).toMatchInlineSnapshot(`
      {
        "cardId": "[id:****************************]",
        "cards": [],
        "createdAt": "[dateTime:]",
        "editor": {},
        "organizationId": "[id:**************************]",
        "regionKey": "page",
        "siteId": "[id:************************]",
        "slug": "test",
        "templateKey": "region",
        "title": "test",
        "updatedAt": "[dateTime:]",
        "userConfig": {},
        "userId": "[id:**************************]",
      }
    `)

    siteObj.addCard({ templateKey: 'area', cardId: testId })
    siteObj.addCard({ templateKey: 'hero', cardId: testId2 })

    await waitFor(200)

    expect(snap(siteObj.layout.page.value?.toConfig())).toMatchInlineSnapshot(`
      {
        "cardId": "[id:****************************]",
        "cards": [
          {
            "cardId": "[id:******_**]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {},
          },
          {
            "cardId": "[id:******_**]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "area",
            "userConfig": {},
          },
        ],
        "createdAt": "[dateTime:]",
        "editor": {},
        "organizationId": "[id:**************************]",
        "regionKey": "page",
        "siteId": "[id:************************]",
        "slug": "test",
        "templateKey": "region",
        "title": "test",
        "updatedAt": "[dateTime:]",
        "userConfig": {},
        "userId": "[id:**************************]",
      }
    `)

    expect(cards?.value?.length).toBe(cardNumInitial + 2)
    expect(cards?.value?.map(c => c.cardId).filter(_ => _).length).toBe(cardNumInitial + 2)

    expect(siteObj.allLayoutCards.value.map(c => c?.cardId.length)).toMatchInlineSnapshot(`
      [
        9,
        9,
      ]
    `)

    siteObj.addCard({ templateKey: 'area', addToCardId: testId })
    siteObj.addCard({ templateKey: 'hero' })

    expect(siteObj.layout.page.value?.cards?.value.find(_ => _.cardId === testId)?.cards.value[0]?.templateKey.value).toBe('area')

    expect(snap(siteObj.layout.page.value?.toConfig(), { maskedKeys: [''] })).toMatchInlineSnapshot(`
      {
        "cardId": "[id:****************************]",
        "cards": [
          {
            "cardId": "[id:*********************]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {},
          },
          {
            "cardId": "[id:******_**]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {},
          },
          {
            "cardId": "[id:******_**]",
            "cards": [
              {
                "cardId": "[id:*********************]",
                "cards": [],
                "depth": "2",
                "parentId": "[id:******_**]",
                "templateKey": "area",
                "userConfig": {},
              },
            ],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "area",
            "userConfig": {},
          },
        ],
        "createdAt": "[dateTime:]",
        "editor": {},
        "organizationId": "[id:**************************]",
        "regionKey": "page",
        "siteId": "[id:************************]",
        "slug": "test",
        "templateKey": "region",
        "title": "test",
        "updatedAt": "[dateTime:]",
        "userConfig": {},
        "userId": "[id:**************************]",
      }
    `)

    const firstCardCards = siteObj.layout.page.value?.cards.value.find(c => c.cardId === testId)?.cards.value ?? []

    expect(firstCardCards.length).toBe(1)
  })

  it('sets correct layout', async () => {
    if (!siteObj || !testUtils?.factorSites)
      throw new Error('missing siteObj or testUtils')

    expect(Object.entries(siteObj.layout).map(([key, comp]) => `${key}-${comp.value?.cards.value.length}`)).toMatchInlineSnapshot(`
      [
        "header-0",
        "page-3",
        "footer-0",
      ]
    `)
  })

  it('handles cards correctly', async () => {
    if (!siteObj || !testUtils?.factorSites)
      throw new Error('missing siteObj or testUtils')

    expect(snap(siteObj.layout.page.value?.toConfig(), { maskedKeys: [''] })).toMatchInlineSnapshot(`
      {
        "cardId": "[id:****************************]",
        "cards": [
          {
            "cardId": "[id:*********************]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {},
          },
          {
            "cardId": "[id:******_**]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {},
          },
          {
            "cardId": "[id:******_**]",
            "cards": [
              {
                "cardId": "[id:*********************]",
                "cards": [],
                "depth": "2",
                "parentId": "[id:******_**]",
                "templateKey": "area",
                "userConfig": {},
              },
            ],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "area",
            "userConfig": {},
          },
        ],
        "createdAt": "[dateTime:]",
        "editor": {},
        "organizationId": "[id:**************************]",
        "regionKey": "page",
        "siteId": "[id:************************]",
        "slug": "test",
        "templateKey": "region",
        "title": "test",
        "updatedAt": "[dateTime:]",
        "userConfig": {},
        "userId": "[id:**************************]",
      }
    `)

    siteObj.addCard({ templateKey: 'marquee', cardId: 'testId_1' })
    siteObj.addCard({ templateKey: 'hero', cardId: 'testId_2' })

    expect(siteObj.editor.stateCardId).toBe('testId_2')
    expect(siteObj.activeCard.value?.cardId).toBe('testId_2')

    siteObj.activeCard.value?.update({ userConfig: { hello: 'world' } })

    expect(siteObj.activeCard.value?.userConfig.value).toMatchInlineSnapshot(`
      {
        "hello": "world",
      }
    `)

    expect(siteObj.activeCard.value?.userConfig.value.hello).toBe('world')

    // make sure region changes don't affect the settings
    siteObj.updateRegion({ cardConfig: { title: 'test' } })
    siteObj.addCard({ templateKey: 'hero', cardId: 'testId_3' })
    siteObj.activeCard.value?.update({ userConfig: { hello: 'world' } })

    siteObj.addCard({ templateKey: 'hero', addToCardId: 'testId_2', cardId: 'nestedTestId1' })
    siteObj.activeCard.value?.update({ userConfig: { foo: 'bar' } })
    expect(siteObj.activeCard.value?.userConfig.value.foo).toBe('bar')

    expect(snap(siteObj.layout.page.value?.toConfig(), { maskedKeys: [''] })).toMatchInlineSnapshot(`
      {
        "cardId": "[id:****************************]",
        "cards": [
          {
            "cardId": "[id:******_*]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {
              "hello": "world",
            },
          },
          {
            "cardId": "[id:******_*]",
            "cards": [
              {
                "cardId": "[id:*************]",
                "cards": [],
                "depth": "2",
                "parentId": "[id:******_*]",
                "templateKey": "hero",
                "userConfig": {
                  "foo": "bar",
                },
              },
            ],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {
              "hello": "world",
            },
          },
          {
            "cardId": "[id:******_*]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "marquee",
            "userConfig": {},
          },
          {
            "cardId": "[id:*********************]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {},
          },
          {
            "cardId": "[id:******_**]",
            "cards": [],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "hero",
            "userConfig": {},
          },
          {
            "cardId": "[id:******_**]",
            "cards": [
              {
                "cardId": "[id:*********************]",
                "cards": [],
                "depth": "2",
                "parentId": "[id:******_**]",
                "templateKey": "area",
                "userConfig": {},
              },
            ],
            "depth": "1",
            "parentId": "[id:****************************]",
            "templateKey": "area",
            "userConfig": {},
          },
        ],
        "createdAt": "[dateTime:]",
        "editor": {},
        "organizationId": "[id:**************************]",
        "regionKey": "page",
        "siteId": "[id:************************]",
        "slug": "test",
        "templateKey": "region",
        "title": "test",
        "updatedAt": "[dateTime:]",
        "userConfig": {},
        "userId": "[id:**************************]",
      }
    `)
  })

  it('saves the site', async () => {
    if (!siteObj || !testUtils?.factorSites)
      throw new Error('missing siteObj or testUtils')

    const responseSiteConfig = await siteObj.requestManageSite({ _action: 'update' })

    expect(responseSiteConfig?.siteId).toBeTruthy()

    const responseSite = new Site({ ...responseSiteConfig, factorSites: testUtils?.factorSites })

    expect(responseSite.regions.value.length).toBe(siteObj.regions.value.length)
  })

  it('deletes the site', async () => {})
})
