import * as os from 'node:os';
import {
    afterEach,
    beforeEach,
    describe,
    expect,
    it,
    vi
} from 'vitest';

import type { RenderContext } from '../../types/RenderContext';
import type { Settings } from '../../types/Settings';
import type { WidgetItem } from '../../types/Widget';
import { CurrentWorkingDirWidget } from '../CurrentWorkingDir';

describe('CurrentWorkingDirWidget', () => {
    const widget = new CurrentWorkingDirWidget();
    const defaultHomeDir = '/Users/alice';
    let mockHomedir: { mockReturnValue: (value: string) => void };

    beforeEach(() => {
        vi.restoreAllMocks();
        mockHomedir = vi.spyOn(os, 'homedir').mockReturnValue(defaultHomeDir);
    });

    afterEach(() => {
        vi.restoreAllMocks();
    });

    const createContext = (cwd?: string, isPreview = false): RenderContext => ({
        data: cwd ? { cwd } : undefined,
        isPreview
    });

    const defaultSettings: Settings = {
        version: 3,
        lines: [],
        flexMode: 'full',
        compactThreshold: 60,
        colorLevel: 2,
        defaultPadding: ' ',
        inheritSeparatorColors: false,
        globalBold: false,
        gitCacheTtlSeconds: 5,
        minimalistMode: false,
        powerline: {
            enabled: false,
            separators: [],
            separatorInvertBackground: [],
            startCaps: [],
            endCaps: [],
            autoAlign: false,
            continueThemeAcrossLines: false
        }
    };

    const createItem = (
        metadata?: Record<string, string>,
        rawValue = false,
        character?: string
    ): WidgetItem => ({
        id: 'test',
        type: 'current-working-dir',
        backgroundColor: 'bgBlue',
        rawValue,
        metadata,
        character
    });

    describe('abbreviateHome', () => {
        it('should replace home directory with ~ when enabled', () => {
            mockHomedir.mockReturnValue(defaultHomeDir);
            const item = createItem({ abbreviateHome: 'true' }, true);
            const result = widget.render(
                item,
                createContext(`${defaultHomeDir}/Documents/Projects`),
                defaultSettings
            );
            expect(result).toBe('~/Documents/Projects');
        });

        it('should not replace home directory when disabled', () => {
            const item = createItem(undefined, true);
            const result = widget.render(
                item,
                createContext(`${defaultHomeDir}/Documents/Projects`),
                defaultSettings
            );
            expect(result).toBe(`${defaultHomeDir}/Documents/Projects`);
        });

        it('should not modify paths outside home directory', () => {
            const item = createItem({ abbreviateHome: 'true' }, true);
            const result = widget.render(
                item,
                createContext('/var/log/app'),
                defaultSettings
            );
            expect(result).toBe('/var/log/app');
        });

        it('should not abbreviate non-home sibling paths with shared prefix', () => {
            mockHomedir.mockReturnValue('/Users/al');

            const item = createItem({ abbreviateHome: 'true' }, true);
            const result = widget.render(
                item,
                createContext('/Users/alex/project'),
                defaultSettings
            );

            expect(result).toBe('/Users/alex/project');
        });

        it('should combine with segments option', () => {
            mockHomedir.mockReturnValue(defaultHomeDir);
            const item = createItem({ abbreviateHome: 'true', segments: '2' }, true);
            const result = widget.render(
                item,
                createContext(`${defaultHomeDir}/Documents/Projects/my-project`),
                defaultSettings
            );
            expect(result).toBe('~/.../Projects/my-project');
        });

        it('should show correct preview when abbreviateHome is enabled', () => {
            const item = createItem({ abbreviateHome: 'true' }, true);
            const result = widget.render(
                item,
                createContext(undefined, true),
                defaultSettings
            );
            expect(result).toBe('~/Documents/Projects/my-project');
        });

        it('should show correct preview when abbreviateHome and segments are enabled', () => {
            const item = createItem({ abbreviateHome: 'true', segments: '2' }, true);
            const result = widget.render(
                item,
                createContext(undefined, true),
                defaultSettings
            );
            expect(result).toBe('~/.../Projects/my-project');
        });

        it('should show correct preview when abbreviateHome and one segment are enabled', () => {
            const item = createItem({ abbreviateHome: 'true', segments: '1' }, true);
            const result = widget.render(
                item,
                createContext(undefined, true),
                defaultSettings
            );
            expect(result).toBe('~/.../my-project');
        });

        it('should preserve windows path separators when combining home abbreviation and segments', () => {
            mockHomedir.mockReturnValue('C:\\Users\\alice');

            const item = createItem({ abbreviateHome: 'true', segments: '2' }, true);
            const result = widget.render(
                item,
                createContext('C:\\Users\\alice\\Documents\\Projects\\app'),
                defaultSettings
            );

            expect(result).toBe('~\\...\\Projects\\app');
        });
    });

    describe('getEditorDisplay', () => {
        it('should show ~ modifier when abbreviateHome is enabled', () => {
            const item = createItem({ abbreviateHome: 'true' });
            const display = widget.getEditorDisplay(item);
            expect(display.modifierText).toBe('(~)');
        });

        it('should show ~ and segments modifiers combined', () => {
            const item = createItem({ abbreviateHome: 'true', segments: '2' });
            const display = widget.getEditorDisplay(item);
            expect(display.modifierText).toBe('(~, segments: 2)');
        });

        it('should not show ~ modifier when fishStyle is enabled', () => {
            const item = createItem({ fishStyle: 'true' });
            const display = widget.getEditorDisplay(item);
            expect(display.modifierText).toBe('(fish-style)');
        });
    });

    describe('handleEditorAction', () => {
        it('should enable abbreviateHome and disable fishStyle', () => {
            const item = createItem({ fishStyle: 'true' });
            const result = widget.handleEditorAction('toggle-abbreviate-home', item);
            expect(result?.metadata?.abbreviateHome).toBe('true');
            expect(result?.metadata?.fishStyle).toBeUndefined();
        });

        it('should disable abbreviateHome when toggled off', () => {
            const item = createItem({ abbreviateHome: 'true' });
            const result = widget.handleEditorAction('toggle-abbreviate-home', item);
            expect(result?.metadata?.abbreviateHome).toBeUndefined();
        });

        it('should enable fishStyle and disable abbreviateHome', () => {
            const item = createItem({ abbreviateHome: 'true' });
            const result = widget.handleEditorAction('toggle-fish-style', item);
            expect(result?.metadata?.fishStyle).toBe('true');
            expect(result?.metadata?.abbreviateHome).toBeUndefined();
        });
    });

    describe('getCustomKeybinds', () => {
        it('should include home ~ keybind', () => {
            const keybinds = widget.getCustomKeybinds();
            const homeKeybind = keybinds.find(k => k.key === 'h');
            expect(homeKeybind).toBeDefined();
            expect(homeKeybind?.label).toBe('(h)ome ~');
            expect(homeKeybind?.action).toBe('toggle-abbreviate-home');
        });

        it('should expose a (g)lyph symbol-override keybind', () => {
            const keybinds = widget.getCustomKeybinds();
            const glyphKeybind = keybinds.find(k => k.key === 'g');
            expect(glyphKeybind).toBeDefined();
            expect(glyphKeybind?.action).toBe('edit-symbol-override');
        });
    });

    describe('glyph symbol override', () => {
        it('renders no glyph prefix by default, leaving the cwd: label intact', () => {
            const item = createItem(undefined, false);
            const result = widget.render(item, createContext('/var/www/site'), defaultSettings);
            expect(result).toBe('cwd: /var/www/site');
        });

        it('prefixes the labeled output with the configured glyph', () => {
            const item = createItem(undefined, false, '📁');
            const result = widget.render(item, createContext('/var/www/site'), defaultSettings);
            expect(result).toBe('📁 cwd: /var/www/site');
        });

        it('keeps the glyph in raw value mode while dropping the cwd: label', () => {
            const item = createItem(undefined, true, '📁');
            const result = widget.render(item, createContext('/var/www/site'), defaultSettings);
            expect(result).toBe('📁 /var/www/site');
        });

        it('composes the glyph with raw value and segment truncation', () => {
            const item = createItem({ segments: '2' }, true, '📁');
            const result = widget.render(item, createContext('/var/www/html/my-project'), defaultSettings);
            expect(result).toBe('📁 .../html/my-project');
        });

        it('shows the glyph in preview mode', () => {
            const item = createItem(undefined, true, '📁');
            const result = widget.render(item, createContext(undefined, true), defaultSettings);
            expect(result).toBe('📁 /Users/example/Documents/Projects/my-project');
        });
    });
});
