import { describe, expect, it } from 'vitest'

import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
import {
  getAssetAdditionalTags,
  getAssetBaseModel,
  getAssetBaseModels,
  getAssetCardTitle,
  getAssetDescription,
  getAssetDisplayFilename,
  getAssetDisplayName,
  getAssetFilename,
  getAssetModelType,
  getAssetSourceUrl,
  getAssetTriggerPhrases,
  getAssetUserDescription,
  getSourceName
} from '@/platform/assets/utils/assetMetadataUtils'

describe('assetMetadataUtils', () => {
  const mockAsset: AssetItem = {
    id: 'test-id',
    name: 'test-model',
    hash: 'hash123',
    size: 1024,
    mime_type: 'application/octet-stream',
    tags: ['models', 'checkpoints'],
    created_at: '2024-01-01T00:00:00Z',
    updated_at: '2024-01-01T00:00:00Z',
    last_access_time: '2024-01-01T00:00:00Z'
  }

  describe('getAssetDescription', () => {
    it.for([
      {
        name: 'returns string description when present',
        description: 'A test model',
        expected: 'A test model'
      },
      { name: 'returns null for non-string', description: 123, expected: null },
      { name: 'returns null for null', description: null, expected: null }
    ])('$name', ({ description, expected }) => {
      const asset = { ...mockAsset, user_metadata: { description } }
      expect(getAssetDescription(asset)).toBe(expected)
    })

    it('should return null when no metadata', () => {
      expect(getAssetDescription(mockAsset)).toBeNull()
    })
  })

  describe('getAssetBaseModel', () => {
    it.for([
      {
        name: 'returns string base_model when present',
        base_model: 'SDXL',
        expected: 'SDXL'
      },
      { name: 'returns null for non-string', base_model: 123, expected: null },
      { name: 'returns null for null', base_model: null, expected: null }
    ])('$name', ({ base_model, expected }) => {
      const asset = { ...mockAsset, user_metadata: { base_model } }
      expect(getAssetBaseModel(asset)).toBe(expected)
    })

    it('should return null when no metadata', () => {
      expect(getAssetBaseModel(mockAsset)).toBeNull()
    })
  })

  describe('getAssetDisplayName', () => {
    it.for([
      {
        name: 'returns name from user_metadata when present',
        user_metadata: { name: 'My Custom Name' },
        display_name: 'ComfyUI_00001_.png',
        expected: 'My Custom Name'
      },
      {
        name: 'returns display_name when user_metadata.name is absent',
        user_metadata: undefined,
        display_name: 'ComfyUI_00001_.png',
        expected: 'ComfyUI_00001_.png'
      },
      {
        name: 'falls back to asset name when both are absent',
        user_metadata: undefined,
        display_name: undefined,
        expected: 'test-model'
      },
      {
        name: 'skips non-string user_metadata.name',
        user_metadata: { name: 123 },
        display_name: 'ComfyUI_00001_.png',
        expected: 'ComfyUI_00001_.png'
      },
      {
        name: 'falls back to asset name when display_name is empty',
        user_metadata: undefined,
        display_name: '',
        expected: 'test-model'
      }
    ])('$name', ({ user_metadata, display_name, expected }) => {
      const asset = { ...mockAsset, user_metadata, display_name }
      expect(getAssetDisplayName(asset)).toBe(expected)
    })
  })

  describe('getAssetSourceUrl', () => {
    it.for([
      {
        name: 'constructs URL from civitai format',
        source_arn: 'civitai:model:123:version:456',
        expected: 'https://civitai.com/models/123?modelVersionId=456'
      },
      { name: 'returns null for non-string', source_arn: 123, expected: null },
      {
        name: 'returns null for unrecognized format',
        source_arn: 'unknown:format',
        expected: null
      }
    ])('$name', ({ source_arn, expected }) => {
      const asset = { ...mockAsset, user_metadata: { source_arn } }
      expect(getAssetSourceUrl(asset)).toBe(expected)
    })

    it('should return null when no metadata', () => {
      expect(getAssetSourceUrl(mockAsset)).toBeNull()
    })
  })

  describe('getAssetTriggerPhrases', () => {
    it.for([
      {
        name: 'returns array when array present',
        trained_words: ['phrase1', 'phrase2'],
        expected: ['phrase1', 'phrase2']
      },
      {
        name: 'wraps single string in array',
        trained_words: 'single phrase',
        expected: ['single phrase']
      },
      {
        name: 'filters non-string values from array',
        trained_words: ['valid', 123, 'also valid', null],
        expected: ['valid', 'also valid']
      }
    ])('$name', ({ trained_words, expected }) => {
      const asset = { ...mockAsset, user_metadata: { trained_words } }
      expect(getAssetTriggerPhrases(asset)).toEqual(expected)
    })

    it('should return empty array when no metadata', () => {
      expect(getAssetTriggerPhrases(mockAsset)).toEqual([])
    })
  })

  describe('getAssetAdditionalTags', () => {
    it.for([
      {
        name: 'returns array of tags when present',
        additional_tags: ['tag1', 'tag2'],
        expected: ['tag1', 'tag2']
      },
      {
        name: 'filters non-string values from array',
        additional_tags: ['valid', 123, 'also valid'],
        expected: ['valid', 'also valid']
      },
      {
        name: 'returns empty array for non-array',
        additional_tags: 'not an array',
        expected: []
      }
    ])('$name', ({ additional_tags, expected }) => {
      const asset = { ...mockAsset, user_metadata: { additional_tags } }
      expect(getAssetAdditionalTags(asset)).toEqual(expected)
    })

    it('should return empty array when no metadata', () => {
      expect(getAssetAdditionalTags(mockAsset)).toEqual([])
    })
  })

  describe('getSourceName', () => {
    it.for([
      {
        name: 'returns Civitai for civitai.com',
        url: 'https://civitai.com/models/123',
        expected: 'Civitai'
      },
      {
        name: 'returns Civitai for civitai.red',
        url: 'https://civitai.red/models/123',
        expected: 'Civitai'
      },
      {
        name: 'returns Hugging Face for huggingface.co',
        url: 'https://huggingface.co/org/model',
        expected: 'Hugging Face'
      },
      {
        name: 'returns Source for unknown URLs',
        url: 'https://example.com/model',
        expected: 'Source'
      }
    ])('$name', ({ url, expected }) => {
      expect(getSourceName(url)).toBe(expected)
    })
  })

  describe('getAssetBaseModels', () => {
    it.for([
      {
        name: 'array of strings',
        base_model: ['SDXL', 'SD1.5', 'Flux'],
        expected: ['SDXL', 'SD1.5', 'Flux']
      },
      {
        name: 'filters non-string entries',
        base_model: ['SDXL', 123, 'SD1.5', null, undefined],
        expected: ['SDXL', 'SD1.5']
      },
      {
        name: 'single string wrapped in array',
        base_model: 'SDXL',
        expected: ['SDXL']
      },
      {
        name: 'non-array/string returns empty',
        base_model: 123,
        expected: []
      },
      { name: 'undefined returns empty', base_model: undefined, expected: [] }
    ])('$name', ({ base_model, expected }) => {
      const asset = { ...mockAsset, user_metadata: { base_model } }
      expect(getAssetBaseModels(asset)).toEqual(expected)
    })

    it('should return empty array when no metadata', () => {
      expect(getAssetBaseModels(mockAsset)).toEqual([])
    })
  })

  describe('getAssetModelType', () => {
    it.for([
      {
        name: 'returns model type from tags',
        tags: ['models', 'checkpoints'],
        expected: 'checkpoints'
      },
      {
        name: 'returns full path for path-style tags',
        tags: ['models', 'diffusers/Kolors/text_encoder'],
        expected: 'diffusers/Kolors/text_encoder'
      },
      {
        name: 'returns null when only models tag',
        tags: ['models'],
        expected: null
      },
      { name: 'returns null when tags empty', tags: [], expected: null }
    ])('$name', ({ tags, expected }) => {
      const asset = { ...mockAsset, tags }
      expect(getAssetModelType(asset)).toBe(expected)
    })
  })

  describe('getAssetUserDescription', () => {
    it.for([
      {
        name: 'returns description when present',
        user_description: 'A custom user description',
        expected: 'A custom user description'
      },
      {
        name: 'returns empty for non-string',
        user_description: 123,
        expected: ''
      },
      { name: 'returns empty for null', user_description: null, expected: '' },
      {
        name: 'returns empty for undefined',
        user_description: undefined,
        expected: ''
      }
    ])('$name', ({ user_description, expected }) => {
      const asset = { ...mockAsset, user_metadata: { user_description } }
      expect(getAssetUserDescription(asset)).toBe(expected)
    })

    it('should return empty string when no metadata', () => {
      expect(getAssetUserDescription(mockAsset)).toBe('')
    })
  })

  describe('getAssetFilename', () => {
    it('returns user_metadata.filename when present', () => {
      const asset = {
        ...mockAsset,
        user_metadata: { filename: 'from_user.png' },
        metadata: { filename: 'from_meta.png' },
        display_name: 'from_display.png'
      }
      expect(getAssetFilename(asset)).toBe('from_user.png')
    })

    it('falls through to metadata.filename then asset.name (never display_name)', () => {
      const asset = {
        ...mockAsset,
        user_metadata: {},
        metadata: {},
        display_name: 'from_display.png'
      }
      expect(getAssetFilename(asset)).toBe(mockAsset.name)
    })
  })

  describe('getAssetDisplayFilename', () => {
    it('prefers user_metadata.filename over everything else', () => {
      const asset = {
        ...mockAsset,
        user_metadata: { filename: 'from_user.png' },
        metadata: { filename: 'from_meta.png' },
        display_name: 'from_display.png'
      }
      expect(getAssetDisplayFilename(asset)).toBe('from_user.png')
    })

    it('falls back to display_name when filename metadata is absent', () => {
      const asset = {
        ...mockAsset,
        user_metadata: {},
        metadata: {},
        display_name: 'ComfyUI_00001_.png'
      }
      expect(getAssetDisplayFilename(asset)).toBe('ComfyUI_00001_.png')
    })

    it('falls back to asset.name when neither filename metadata nor display_name exist', () => {
      expect(getAssetDisplayFilename(mockAsset)).toBe(mockAsset.name)
    })
  })

  describe('getAssetCardTitle', () => {
    it('returns user_metadata.name when it differs from asset.name', () => {
      const asset = {
        ...mockAsset,
        name: 'lora_v1.safetensors',
        user_metadata: { name: 'My Favorite LoRA' },
        metadata: { filename: 'lora_v1.safetensors' }
      }
      expect(getAssetCardTitle(asset)).toBe('My Favorite LoRA')
    })

    it('returns metadata.name when user_metadata.name is absent and it differs from asset.name', () => {
      const asset = {
        ...mockAsset,
        name: 'model_file.safetensors',
        metadata: { name: 'Curated Model' }
      }
      expect(getAssetCardTitle(asset)).toBe('Curated Model')
    })

    it('falls through to the filename helper when curated name equals asset.name (hash case)', () => {
      const HASH = 'blake3:abc'
      const asset = {
        ...mockAsset,
        name: HASH,
        user_metadata: { name: HASH },
        metadata: { filename: 'sunset.png' }
      }
      expect(getAssetCardTitle(asset)).toBe('sunset.png')
    })

    it('falls through to display_name when neither curated name nor filename metadata exist', () => {
      const asset = {
        ...mockAsset,
        name: 'hash.png',
        display_name: 'pretty.png'
      }
      expect(getAssetCardTitle(asset)).toBe('pretty.png')
    })
  })
})
