import { AppDataComplete } from '../model/model-config';
import { OpLog } from '../../core/log';
import { TODAY_TAG } from '../../features/tag/tag.const';
import { OP_LOG_SYNC_LOGGER } from '../core/sync-logger.adapter';

describe('isRelatedModelDataValid', () => {
  let isRelatedModelDataValid: any;

  beforeEach(() => {
    // Suppress OpLog output during tests by spying on the methods
    spyOn(OpLog, 'log');
    spyOn(OpLog, 'info');
    spyOn(OpLog, 'error');
    spyOn(OpLog, 'warn');
    spyOn(OpLog, 'err');
    spyOn(OpLog, 'critical');
    spyOn(OP_LOG_SYNC_LOGGER, 'log');
    /* eslint-disable @typescript-eslint/no-require-imports */
    // Reset modules to allow re-importing with mocks
    // @ts-ignore
    delete require.cache[require.resolve('../../util/dev-error')];
    // @ts-ignore
    delete require.cache[require.resolve('./is-related-model-data-valid')];

    // Mock the devError module
    // @ts-ignore
    require('../../util/dev-error');
    // @ts-ignore
    require.cache[require.resolve('../../util/dev-error')] = {
      exports: {
        devError: jasmine.createSpy('devError'),
      },
    };

    // Import the function under test
    // @ts-ignore
    isRelatedModelDataValid =
      require('./is-related-model-data-valid').isRelatedModelDataValid;
    /* eslint-enable @typescript-eslint/no-require-imports */
  });

  it('should handle null data gracefully', () => {
    // @ts-ignore
    const result = isRelatedModelDataValid(null);
    expect(result).toBe(false);
  });

  it('should handle partial null data gracefully', () => {
    const partialData: any = {
      project: { ids: [], entities: {} },
      tag: { ids: [], entities: {} },
      task: { ids: [], entities: {} },
      // Missing archiveYoung and archiveOld
      note: { ids: [], entities: {} },
      issueProvider: { ids: [], entities: {} },
      reminders: [],
    };

    const result = isRelatedModelDataValid(partialData as AppDataComplete);
    expect(result).toBe(false);
  });

  it('should log validity error metadata without raw app data', () => {
    const privateProjectTitle = 'Private Project Title';
    const data: any = {
      project: {
        ids: ['p1'],
        entities: {
          p1: {
            id: 'p1',
            title: privateProjectTitle,
            taskIds: ['missing-task'],
            backlogTaskIds: [],
            noteIds: [],
          },
        },
      },
      tag: { ids: [], entities: {} },
      task: { ids: [], entities: {} },
      taskRepeatCfg: { ids: [], entities: {} },
      archiveYoung: { task: { ids: [], entities: {} } },
      archiveOld: { task: { ids: [], entities: {} } },
      note: { ids: [], entities: {}, todayOrder: [] },
      issueProvider: { ids: [], entities: {} },
      reminders: [],
      menuTree: { projectTree: [], tagTree: [] },
    };

    const result = isRelatedModelDataValid(data as AppDataComplete);

    expect(result).toBe(false);
    expect(OP_LOG_SYNC_LOGGER.log).toHaveBeenCalledWith(
      '[is-related-model-data-valid] Validity error info',
      jasmine.objectContaining({
        error: 'Missing task data for project',
        tid: 'missing-task',
        projectId: 'p1',
      }),
    );
    expect(
      JSON.stringify((OP_LOG_SYNC_LOGGER.log as jasmine.Spy).calls.allArgs()),
    ).not.toContain(privateProjectTitle);
  });

  it('should not log raw invalid menu tree node kinds', () => {
    const privateNodeKind = 'Private Invalid Node Kind';
    const data: any = {
      project: { ids: [], entities: {} },
      tag: { ids: [], entities: {} },
      task: { ids: [], entities: {} },
      taskRepeatCfg: { ids: [], entities: {} },
      archiveYoung: { task: { ids: [], entities: {} } },
      archiveOld: { task: { ids: [], entities: {} } },
      note: { ids: [], entities: {}, todayOrder: [] },
      issueProvider: { ids: [], entities: {} },
      reminders: [],
      menuTree: {
        projectTree: [{ id: 'tree-node-id', k: privateNodeKind }],
        tagTree: [],
      },
    };

    const result = isRelatedModelDataValid(data as AppDataComplete);

    expect(result).toBe(false);
    expect(OP_LOG_SYNC_LOGGER.log).toHaveBeenCalledWith(
      '[is-related-model-data-valid] Validity error info',
      jasmine.objectContaining({
        error: 'Invalid node kind in projectTree',
        treeType: 'projectTree',
        id: 'tree-node-id',
        nodeKind: 'unknown',
      }),
    );
    expect(
      JSON.stringify((OP_LOG_SYNC_LOGGER.log as jasmine.Spy).calls.allArgs()),
    ).not.toContain(privateNodeKind);
  });

  it('should fail validation when task has non-existent repeatCfgId', () => {
    const dataWithOrphanedRepeatCfgId: any = {
      project: {
        ids: ['p1'],
        entities: {
          p1: { id: 'p1', taskIds: ['t1'], backlogTaskIds: [], noteIds: [] },
        },
      },
      tag: { ids: [], entities: {} },
      task: {
        ids: ['t1'],
        entities: {
          t1: {
            id: 't1',
            projectId: 'p1',
            tagIds: [],
            subTaskIds: [],
            repeatCfgId: 'NON_EXISTENT_CFG',
          },
        },
      },
      taskRepeatCfg: { ids: [], entities: {} },
      archiveYoung: { task: { ids: [], entities: {} } },
      archiveOld: { task: { ids: [], entities: {} } },
      note: { ids: [], entities: {}, todayOrder: [] },
      issueProvider: { ids: [], entities: {} },
      reminders: [],
      menuTree: { projectTree: [], tagTree: [] },
    };

    const result = isRelatedModelDataValid(dataWithOrphanedRepeatCfgId);
    expect(result).toBe(false);
  });

  it('should pass validation when task has valid repeatCfgId', () => {
    const dataWithValidRepeatCfgId: any = {
      project: {
        ids: ['p1'],
        entities: {
          p1: { id: 'p1', taskIds: ['t1'], backlogTaskIds: [], noteIds: [] },
        },
      },
      tag: { ids: [], entities: {} },
      task: {
        ids: ['t1'],
        entities: {
          t1: {
            id: 't1',
            projectId: 'p1',
            tagIds: [],
            subTaskIds: [],
            repeatCfgId: 'validRepeatCfg',
          },
        },
      },
      taskRepeatCfg: {
        ids: ['validRepeatCfg'],
        entities: { validRepeatCfg: { id: 'validRepeatCfg' } },
      },
      archiveYoung: { task: { ids: [], entities: {} } },
      archiveOld: { task: { ids: [], entities: {} } },
      note: { ids: [], entities: {}, todayOrder: [] },
      issueProvider: { ids: [], entities: {} },
      reminders: [],
      menuTree: { projectTree: [], tagTree: [] },
    };

    const result = isRelatedModelDataValid(dataWithValidRepeatCfgId);
    expect(result).toBe(true);
  });

  describe('stale references in archived tasks (issue #6270)', () => {
    const baseData = (): any => ({
      project: {
        ids: ['p1'],
        entities: { p1: { id: 'p1', taskIds: [], backlogTaskIds: [], noteIds: [] } },
      },
      tag: { ids: [], entities: {} },
      task: { ids: [], entities: {} },
      taskRepeatCfg: { ids: [], entities: {} },
      archiveYoung: { task: { ids: [], entities: {} } },
      archiveOld: { task: { ids: [], entities: {} } },
      note: { ids: [], entities: {}, todayOrder: [] },
      issueProvider: { ids: [], entities: {} },
      reminders: [],
      menuTree: { projectTree: [], tagTree: [] },
    });

    const cases = [
      {
        archive: 'archiveYoung' as const,
        field: 'repeatCfgId',
        taskOverride: { repeatCfgId: 'DELETED_REPEAT_CFG' },
        logFragment: 'stale repeatCfgId',
      },
      {
        archive: 'archiveOld' as const,
        field: 'repeatCfgId',
        taskOverride: { repeatCfgId: 'DELETED_REPEAT_CFG' },
        logFragment: 'stale repeatCfgId',
      },
      {
        archive: 'archiveYoung' as const,
        field: 'projectId',
        taskOverride: { projectId: 'NON_EXISTENT_PROJECT' },
        logFragment: 'stale projectId',
      },
      {
        archive: 'archiveOld' as const,
        field: 'projectId',
        taskOverride: { projectId: 'NON_EXISTENT_PROJECT' },
        logFragment: 'stale projectId',
      },
      {
        archive: 'archiveYoung' as const,
        field: 'tagId',
        taskOverride: { tagIds: ['NON_EXISTENT_TAG'] },
        logFragment: 'stale tagId',
      },
      {
        archive: 'archiveOld' as const,
        field: 'tagId',
        taskOverride: { tagIds: ['NON_EXISTENT_TAG'] },
        logFragment: 'stale tagId',
      },
    ];

    cases.forEach(({ archive, field, taskOverride, logFragment }) => {
      it(`should pass when ${archive} task has stale ${field} and log warning`, () => {
        const data = baseData();
        data[archive].task = {
          ids: ['t1'],
          entities: {
            t1: {
              id: 't1',
              projectId: 'p1',
              tagIds: [],
              subTaskIds: [],
              ...taskOverride,
            },
          },
        };

        expect(isRelatedModelDataValid(data)).toBe(true);
        expect(OpLog.info).toHaveBeenCalledWith(
          jasmine.stringContaining(
            `${archive} has 1 tasks with ${logFragment} (harmless)`,
          ),
          jasmine.any(Object),
        );
      });
    });
  });

  describe('TODAY_TAG orphaned IDs handling', () => {
    it('should pass validation for TODAY_TAG with orphaned task IDs and log warning', () => {
      const dataWithTodayTagOrphans: any = {
        project: { ids: [], entities: {} },
        tag: {
          ids: [TODAY_TAG.id],
          entities: {
            [TODAY_TAG.id]: {
              id: TODAY_TAG.id,
              title: TODAY_TAG.title,
              taskIds: ['orphan-task-1', 'orphan-task-2'], // These tasks don't exist
            },
          },
        },
        task: { ids: [], entities: {} }, // No tasks
        taskRepeatCfg: { ids: [], entities: {} },
        archiveYoung: { task: { ids: [], entities: {} } },
        archiveOld: { task: { ids: [], entities: {} } },
        note: { ids: [], entities: {}, todayOrder: [] },
        issueProvider: { ids: [], entities: {} },
        reminders: [],
        menuTree: { projectTree: [], tagTree: [] },
      };

      const result = isRelatedModelDataValid(dataWithTodayTagOrphans);

      // Should pass because TODAY_TAG orphans are harmless (virtual tag)
      expect(result).toBe(true);
      // Verify warning was logged
      expect(OpLog.info).toHaveBeenCalledWith(
        jasmine.stringContaining('TODAY_TAG has 2 orphaned task IDs (harmless)'),
        jasmine.any(Object),
      );
      // Validation should NOT mutate the data (read-only)
      expect(dataWithTodayTagOrphans.tag.entities[TODAY_TAG.id].taskIds).toEqual([
        'orphan-task-1',
        'orphan-task-2',
      ]);
    });

    it('should pass validation for TODAY_TAG with mixed valid and orphaned task IDs', () => {
      const dataWithMixedTodayTag: any = {
        project: { ids: [], entities: {} },
        tag: {
          ids: [TODAY_TAG.id, 'regularTag'],
          entities: {
            [TODAY_TAG.id]: {
              id: TODAY_TAG.id,
              title: TODAY_TAG.title,
              taskIds: ['validTask', 'orphanTask'], // Mixed valid and orphan
            },
            regularTag: {
              id: 'regularTag',
              title: 'Regular Tag',
              taskIds: ['validTask'],
            },
          },
        },
        task: {
          ids: ['validTask'],
          entities: {
            validTask: {
              id: 'validTask',
              projectId: null,
              tagIds: ['regularTag'],
              subTaskIds: [],
              parentId: null,
            },
          },
        },
        taskRepeatCfg: { ids: [], entities: {} },
        archiveYoung: { task: { ids: [], entities: {} } },
        archiveOld: { task: { ids: [], entities: {} } },
        note: { ids: [], entities: {}, todayOrder: [] },
        issueProvider: { ids: [], entities: {} },
        reminders: [],
        menuTree: { projectTree: [], tagTree: [] },
      };

      const result = isRelatedModelDataValid(dataWithMixedTodayTag);

      expect(result).toBe(true);
      // Validation should NOT mutate the data (read-only)
      expect(dataWithMixedTodayTag.tag.entities[TODAY_TAG.id].taskIds).toEqual([
        'validTask',
        'orphanTask',
      ]);
    });

    it('should fail validation for regular tags with orphaned task IDs', () => {
      const dataWithRegularTagOrphans: any = {
        project: { ids: [], entities: {} },
        tag: {
          ids: ['regularTag'],
          entities: {
            regularTag: {
              id: 'regularTag',
              title: 'Regular Tag',
              taskIds: ['orphanTask'], // This task doesn't exist
            },
          },
        },
        task: { ids: [], entities: {} }, // No tasks
        taskRepeatCfg: { ids: [], entities: {} },
        archiveYoung: { task: { ids: [], entities: {} } },
        archiveOld: { task: { ids: [], entities: {} } },
        note: { ids: [], entities: {}, todayOrder: [] },
        issueProvider: { ids: [], entities: {} },
        reminders: [],
        menuTree: { projectTree: [], tagTree: [] },
      };

      const result = isRelatedModelDataValid(dataWithRegularTagOrphans);

      // Should fail because regular tags don't get self-healing
      expect(result).toBe(false);
    });

    it('should not log warning when TODAY_TAG has no orphaned IDs', () => {
      const dataWithValidTodayTag: any = {
        project: {
          ids: ['p1'],
          entities: {
            p1: { id: 'p1', taskIds: ['validTask'], backlogTaskIds: [], noteIds: [] },
          },
        },
        tag: {
          ids: [TODAY_TAG.id],
          entities: {
            [TODAY_TAG.id]: {
              id: TODAY_TAG.id,
              title: TODAY_TAG.title,
              taskIds: ['validTask'],
            },
          },
        },
        task: {
          ids: ['validTask'],
          entities: {
            validTask: {
              id: 'validTask',
              projectId: 'p1',
              tagIds: [],
              subTaskIds: [],
              parentId: null,
            },
          },
        },
        taskRepeatCfg: { ids: [], entities: {} },
        archiveYoung: { task: { ids: [], entities: {} } },
        archiveOld: { task: { ids: [], entities: {} } },
        note: { ids: [], entities: {}, todayOrder: [] },
        issueProvider: { ids: [], entities: {} },
        reminders: [],
        menuTree: { projectTree: [], tagTree: [] },
      };

      const result = isRelatedModelDataValid(dataWithValidTodayTag);

      expect(result).toBe(true);
      // Should not log any self-healing warning
      expect(OpLog.warn).not.toHaveBeenCalled();
    });
  });
});
