import { Alignment, pad } from '../../../string'
import {
  EMERGENCY_PASSAGE,
  IGS,
  RUM_DESTINATION,
  RUM_ENTRY_MODE,
  RUM_EXIT_MODE,
  RUMObject,
  SEX,
  ZASection
} from '../../../types'
import {
  DEFAULT_022_RSS_FORMAT,
  DEFAULT_ADM,
  DEFAULT_AGE_GESTATION,
  DEFAULT_AUTHORISATION,
  DEFAULT_BIRTH_DATE,
  DEFAULT_CAT,
  DEFAULT_CONF,
  DEFAULT_CONV,
  DEFAULT_CP_RES,
  DEFAULT_DESTINATION,
  DEFAULT_DP,
  DEFAULT_DR,
  DEFAULT_ENTRY_DATE,
  DEFAULT_ENTRY_DATE_ZA,
  DEFAULT_ENTRY_MODE,
  DEFAULT_ER,
  DEFAULT_EXIT_DATE,
  DEFAULT_EXIT_MODE,
  DEFAULT_FILL,
  DEFAULT_FINESS,
  DEFAULT_IGS,
  DEFAULT_INNOV,
  DEFAULT_LAST_MENS_DATE,
  DEFAULT_N_DA,
  DEFAULT_N_DAD,
  DEFAULT_N_ZA,
  DEFAULT_NP,
  DEFAULT_PROVENANCE,
  DEFAULT_RAAC,
  DEFAULT_RES,
  DEFAULT_RH,
  DEFAULT_RSS,
  DEFAULT_RT_D,
  DEFAULT_RT_M,
  DEFAULT_RUM,
  DEFAULT_SESSION,
  DEFAULT_SEX,
  DEFAULT_SPEC,
  DEFAULT_TARIF,
  DEFAULT_U_MED,
  DEFAULT_WEIGHT
} from '../../shared'
import { parseRUMObjectTo022FormatString } from './022-format'

describe('parseRUMObjectTo022FormatString', () => {
  describe('with default parameters', () => {
    it('should use default values when no parameters provided', () => {
      const result = parseRUMObjectTo022FormatString({})

      // Check some key default values are present in correct positions
      expect(result.substring(0, 9)).toBe(DEFAULT_FINESS) // FINESS padded
      expect(result.substring(9, 12)).toBe(DEFAULT_022_RSS_FORMAT) // Format version
      expect(result.substring(12, 32)).toBe(pad(DEFAULT_RSS, 20)) // RSS padded left
      expect(result.substring(32, 52)).toBe(pad(DEFAULT_ADM, 20)) // ADM padded left
    })

    it('should generate a record with all default values', () => {
      const result = parseRUMObjectTo022FormatString({})

      // The result should be a long string with all default values formatted
      expect(typeof result).toBe('string')
      expect(result.length).toBeGreaterThan(0)

      // Test that it starts with the expected FINESS and version
      expect(result.startsWith(DEFAULT_FINESS + DEFAULT_022_RSS_FORMAT)).toBe(true)

      const expectedFixed = [
        pad(DEFAULT_FINESS, 9, Alignment.LEFT),
        DEFAULT_022_RSS_FORMAT,
        pad(DEFAULT_RSS, 20, Alignment.LEFT),
        pad(DEFAULT_ADM, 20, Alignment.LEFT),
        pad(DEFAULT_RUM, 10, Alignment.LEFT),
        DEFAULT_BIRTH_DATE,
        DEFAULT_SEX,
        pad(DEFAULT_U_MED, 4, Alignment.LEFT),
        pad(DEFAULT_AUTHORISATION ?? '', 2, Alignment.LEFT),
        DEFAULT_ENTRY_DATE,
        DEFAULT_ENTRY_MODE,
        pad(DEFAULT_PROVENANCE, 1, Alignment.LEFT),
        DEFAULT_EXIT_DATE,
        DEFAULT_EXIT_MODE,
        pad(DEFAULT_DESTINATION, 1, Alignment.LEFT),
        DEFAULT_CP_RES,
        pad(DEFAULT_WEIGHT, 4, Alignment.RIGHT, ' '),
        pad(DEFAULT_AGE_GESTATION, 2, Alignment.RIGHT, ' '),
        pad(DEFAULT_LAST_MENS_DATE, 8, Alignment.LEFT, ' '),
        pad(DEFAULT_SESSION.toString(), 2, Alignment.RIGHT, '0'),
        pad(DEFAULT_N_DA.toString(), 2, Alignment.RIGHT, '0'),
        pad(DEFAULT_N_DAD.toString(), 2, Alignment.RIGHT, '0'),
        pad(DEFAULT_N_ZA.toString(), 3, Alignment.RIGHT, '0'),
        pad(DEFAULT_DP, 8, Alignment.LEFT),
        pad(DEFAULT_DR, 8, Alignment.LEFT),
        pad(DEFAULT_IGS, 3, Alignment.RIGHT, '0'),
        pad(DEFAULT_CONF ?? '', 1, Alignment.LEFT),
        pad(DEFAULT_RT_M, 1, Alignment.LEFT),
        pad(DEFAULT_RT_D, 1, Alignment.LEFT),
        pad(DEFAULT_INNOV, 15, Alignment.LEFT),
        pad(DEFAULT_CONV, 1, Alignment.LEFT),
        pad(DEFAULT_RAAC, 1, Alignment.LEFT),
        pad(DEFAULT_SPEC, 1, Alignment.LEFT),
        pad(DEFAULT_RH, 1, Alignment.LEFT),
        pad(DEFAULT_TARIF, 1, Alignment.LEFT),
        pad(DEFAULT_CAT, 1, Alignment.LEFT),
        pad(DEFAULT_NP, 1, Alignment.LEFT),
        pad(DEFAULT_ER, 1, Alignment.LEFT),
        pad(DEFAULT_FILL, 4, Alignment.LEFT),
        pad(DEFAULT_RES, 3, Alignment.LEFT)
      ].join('')

      const expectedDAs = [].join('')
      const expectedDADs = [].join('')
      const expectedZAs = [].join('')

      const expectedResult = expectedFixed + expectedDAs + expectedDADs + expectedZAs

      expect(result).toBe(expectedResult)
    })
  })

  describe('with custom parameters', () => {
    it('should use provided FINESS value', () => {
      const customFINESS = '987654321'
      const result = parseRUMObjectTo022FormatString({ FINESS: customFINESS })

      expect(result.substring(0, 9)).toBe(customFINESS)
    })

    it('should use provided RSS value with proper padding', () => {
      const customRSS = 'CUSTOM'
      const result = parseRUMObjectTo022FormatString({ RSS: customRSS })

      expect(result.substring(12, 32)).toBe('CUSTOM              ') // padded left to 20 chars
    })

    it('should use provided numeric values correctly', () => {
      const params: RUMObject = {
        nDA: 5,
        nDAD: 3,
        nZA: 2
      }
      const result = parseRUMObjectTo022FormatString(params)

      // These values should be padded right with zeros to 2 or 3 digits
      expect(result).toContain('05') // nDA padded to 2 digits
      expect(result).toContain('03') // nDAD padded to 2 digits
      expect(result).toContain('002') // nZA padded to 3 digits
    })

    it('should handle weight with zero padding', () => {
      const params: RUMObject = {
        weight: '75'
      }
      const result = parseRUMObjectTo022FormatString(params)

      // Weight should be padded right with zeros to 4 digits
      expect(result).toContain('0075')
    })
  })

  describe('with DA codes', () => {
    it('should format DAs array correctly', () => {
      const params: RUMObject = {
        DAs: ['CODE1', 'CODE2', 'CODE3']
      }
      const result = parseRUMObjectTo022FormatString(params)

      // Each DA code should be padded left to 8 characters
      expect(result).toContain('   CODE1   CODE2   CODE3')
    })

    it('should handle empty DAs array', () => {
      const params: RUMObject = {
        DAs: []
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(typeof result).toBe('string')
      expect(result.length).toBeGreaterThan(0)
    })

    it('should handle exactly 8-character DA codes', () => {
      const params: RUMObject = {
        DAs: ['12345678', 'ABCDEFGH']
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(result).toContain('12345678ABCDEFGH')
    })
  })

  describe('with DAD codes', () => {
    it('should format DADs array correctly', () => {
      const params: RUMObject = {
        DADs: ['DAD1', 'DAD2']
      }
      const result = parseRUMObjectTo022FormatString(params)

      // Each DAD code should be padded left to 8 characters
      expect(result).toContain('    DAD1    DAD2')
    })

    it('should handle single DAD code', () => {
      const params: RUMObject = {
        DADs: ['SINGLE']
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(result).toContain('  SINGLE')
    })
  })

  describe('with ZA sections', () => {
    it('should format ZAs array correctly with all fields', () => {
      const zaSection: ZASection = {
        date: DEFAULT_ENTRY_DATE,
        code: 'HHFA001',
        ext: '01',
        phase: 'A',
        act: '1',
        diag: '0',
        mod: 'MOD1',
        RE: '1',
        AN: '0',
        qty: '05'
      }

      const params: RUMObject = {
        ZAs: [zaSection]
      }
      const result = parseRUMObjectTo022FormatString(params)

      // Check the ZA section is properly formatted (29 characters total)
      expect(result).toContain(`${DEFAULT_ENTRY_DATE}HHFA001-01A10MOD11005`)
    })

    it('should handle ZA sections with missing optional fields', () => {
      const zaSection: ZASection = {
        date: DEFAULT_ENTRY_DATE,
        code: 'HHFA001'
      }

      const params: RUMObject = {
        ZAs: [zaSection]
      }
      const result = parseRUMObjectTo022FormatString(params)

      // Optional fields should be padded with spaces
      expect(result).toContain(`${DEFAULT_ENTRY_DATE}HHFA001`)
    })

    it('should handle multiple ZA sections', () => {
      const zaSection1: ZASection = {
        date: DEFAULT_ENTRY_DATE,
        code: 'ZZZ1111',
        qty: '01'
      }

      const zaSection2: ZASection = {
        date: DEFAULT_ENTRY_DATE_ZA,
        code: 'ZZZ2222',
        qty: '02'
      }

      const params: RUMObject = {
        ZAs: [zaSection1, zaSection2]
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(result).toContain(`${DEFAULT_ENTRY_DATE}ZZZ1111`)
      expect(result).toContain(`${DEFAULT_ENTRY_DATE_ZA}ZZZ2222`)
    })

    it('should pad qty field with zeros', () => {
      const zaSection: ZASection = {
        date: DEFAULT_ENTRY_DATE,
        code: 'HHFA001',
        qty: '5'
      }

      const params: RUMObject = {
        ZAs: [zaSection]
      }
      const result = parseRUMObjectTo022FormatString(params)

      // qty should be padded right with zeros to 2 digits
      expect(result).toContain('05')
    })
  })

  describe('field formatting and lengths', () => {
    it('should handle date fields correctly', () => {
      const params: RUMObject = {
        birthDate: '15061985',
        entryDate: '10052024',
        exitDate: '15052024',
        lastMensDate: '01042024'
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(result).toContain('15061985')
      expect(result).toContain('10052024')
      expect(result).toContain('15052024')
      expect(result).toContain('01042024')
    })

    it('should handle character fields with proper padding', () => {
      const params: RUMObject = {
        sex: SEX.FEMALE,
        entryMode: RUM_ENTRY_MODE.ENTRY_MODE_8,
        exitMode: RUM_EXIT_MODE.EXIT_MODE_7
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(result).toContain('2')
      expect(result).toContain('8')
      expect(result).toContain('7')
    })

    it('should pad ageGestation with zeros', () => {
      const params: RUMObject = {
        ageGestation: '5'
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(result).toContain('05')
    })

    it('should pad session with zeros', () => {
      const params: RUMObject = {
        session: 5
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(result).toContain('05')
    })

    it('should pad IGS with zeros', () => {
      const params: RUMObject = {
        IGS: IGS.IGS_EMPTY
      }
      const result = parseRUMObjectTo022FormatString(params)

      expect(result).toContain('001')
    })
  })

  describe('complete record formatting', () => {
    it('should generate a complete record with mixed parameters', () => {
      const params: RUMObject = {
        FINESS: '111222333',
        RSS: 'TESTRSS',
        birthDate: '01011990',
        sex: SEX.FEMALE,
        weight: '3500',
        nDA: 2,
        nDAD: 1,
        nZA: 1,
        DAs: ['TESTDA01', 'TESTDA02'],
        DADs: ['TESTDAD1'],
        ZAs: [
          {
            date: DEFAULT_ENTRY_DATE,
            code: 'TEST123',
            ext: '02',
            phase: 'B',
            act: '2',
            diag: '1',
            mod: 'TEST',
            RE: '0',
            AN: '1',
            qty: '03'
          }
        ]
      }

      const result = parseRUMObjectTo022FormatString(params)

      expect(typeof result).toBe('string')
      expect(result.length).toBeGreaterThan(100) // Should be a substantial string
      expect(result.startsWith(params.FINESS + DEFAULT_022_RSS_FORMAT)).toBe(true)
      expect(result).toContain(params.DAs?.join('') ?? '')
      expect(result).toContain(params.DADs?.join('') ?? '')
      expect(result).toContain(`${DEFAULT_ENTRY_DATE}`)
      expect(result).toContain(`TEST123-02B21TEST0103`)
    })
  })

  describe('edge cases', () => {
    it('should handle undefined parameters gracefully', () => {
      const params: RUMObject = {
        FINESS: undefined,
        RSS: undefined
      }

      expect(() => parseRUMObjectTo022FormatString(params)).not.toThrow()
    })

    it('should handle empty string parameters', () => {
      const params: RUMObject = {
        FINESS: '',
        RSS: '',
        weight: ''
      }

      const result = parseRUMObjectTo022FormatString(params)
      expect(typeof result).toBe('string')
    })

    it('should handle very large numbers for counts', () => {
      const params: RUMObject = {
        nDA: 99,
        nDAD: 99,
        nZA: 999
      }

      const result = parseRUMObjectTo022FormatString(params)
      expect(result).toContain('99')
      expect(result).toContain('999')
    })
  })

  describe('hardcoded test case', () => {
    it('should format the hardcoded RUM object correctly', () => {
      // Hardcoded RUM object from test-grp2024.ts
      const hardcodedRumObject: RUMObject = {
        FINESS: '590781415',
        RSS: '2078952',
        RUM: '207895201',
        birthDate: '20041948',
        sex: SEX.FEMALE,
        uMed: '7166',
        entryDate: '14122024',
        entryMode: RUM_ENTRY_MODE.ENTRY_MODE_8,
        exitDate: '15122024',
        exitMode: RUM_EXIT_MODE.EXIT_MODE_6,
        destination: RUM_DESTINATION.DESTINATION_1,
        cpRes: '59229',
        weight: '',
        ageGestation: '',
        session: 0,
        nDA: 4,
        nDAD: 0,
        nZA: 12,
        IGS: '084',
        ER: EMERGENCY_PASSAGE.EMERGENCY_PASSAGE_5,
        DP: 'I442',
        DAs: ['R55', 'I10', 'C911', 'I350'],
        DADs: [],
        ZAs: [
          {
            date: '15122024',
            code: 'DELF011',
            phase: '0',
            act: '1',
            mod: 'U',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'DERP004',
            phase: '0',
            act: '1',
            mod: 'F',
            RE: 'N',
            AN: '5',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'EQLF002',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'EQLF002',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          },
          {
            date: '14122024',
            code: 'DEQP003',
            phase: '0',
            act: '1',
            mod: 'U',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'DEQP007',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'DEQP007',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'ENLF001',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'EQLF001',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'GLLD017',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'GLLD017',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          },
          {
            date: '15122024',
            code: 'YYYY105',
            phase: '0',
            act: '1',
            RE: 'N',
            qty: '01'
          }
        ]
      }

      const result = parseRUMObjectTo022FormatString(hardcodedRumObject)

      const expectedOutput =
        '207895201 2004194827166  141220248 151220246159229              000400012I442            084                         5       R55     I10     C911    I350    15122024DELF011   01 U   N 0115122024DERP004   01 F   N50115122024EQLF002   01     N 0115122024EQLF002   01     N 0114122024DEQP003   01 U   N 0115122024DEQP007   01     N 0115122024DEQP007   01     N 0115122024ENLF001   01     N 0115122024EQLF001   01     N 0115122024GLLD017   01     N 0115122024GLLD017   01     N 0115122024YYYY105   01     N 01'

      expect(result.slice(52, 52 + expectedOutput.length)).toBe(expectedOutput)
    })
  })
})
