/*
 * MIT License
 *
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
import { Expression } from "@ohos/expr-parser";

export default function abilityTest() {
  describe('ActsAbilityTest', () => {
    // Defines a test suite. Two parameters are supported: test suite name and test suite function.
    beforeAll(() => {
      // Presets an action, which is performed only once before all test cases of the test suite start.
      // This API supports only one parameter: preset action function.
    })
    beforeEach(() => {
      // Presets an action, which is performed before each unit test case starts.
      // The number of execution times is the same as the number of test cases defined by **it**.
      // This API supports only one parameter: preset action function.
    })
    afterEach(() => {
      // Presets a clear action, which is performed after each unit test case ends.
      // The number of execution times is the same as the number of test cases defined by **it**.
      // This API supports only one parameter: clear action function.
    })
    afterAll(() => {
      // Presets a clear action, which is performed after all test cases of the test suite end.
      // This API supports only one parameter: clear action function.
    })

    it('exprParser_parse_normal', 0, () => {
      let expr: Function = new Expression('"\\u2605haha"').parse();
      expect(expr({})).assertEqual('★haha');

      expr = new Expression('"\\n\\r\\f\\t\\v\\s"').parse();
      expect(expr({})).assertEqual('\n\r\f\t\v\s');

      expr = new Expression('2e3').parse();
      expect(expr({})).assertEqual(2000);

      expr = new Expression('2e-3').parse();
      expect(expr({})).assertEqual(0.002);

      expr = new Expression('+"123"').parse();
      expect(expr({})).assertEqual('123');

      expr = new Expression('-123').parse();
      expect(expr({})).assertEqual(-123);

      expr = new Expression('a.b.c.d').parse();
      class B {
        b: null = null;
      }
      expect(expr({ a: { b: null } as B })).assertNull();

      expr = new Expression('{ a: {}, b: [] }').parse();
      expect(expr({})).assertDeepEquals({ a: {}, b: [] });

      expr = new Expression(']123').parse();
      expect(expr({})).assertEqual(undefined);
    })

    it('exprParser_parseConstants', 0, () => {
      let expr: Function = new Expression('a === null').parse();
      expect(expr({ a: null })).assertEqual(true);
      expect(expr({ a: 1 })).assertEqual(false);

      expr = new Expression('a === undefined').parse();
      expect(expr({ a: undefined })).assertEqual(true);
      expect(expr({ a: 1 })).assertEqual(false);

      expr = new Expression('a === true').parse();
      expect(expr({ a: true })).assertEqual(true);
      expect(expr({ a: 1 })).assertEqual(false);

      expr = new Expression('a === false').parse();
      expect(expr({ a: false })).assertEqual(true);
      expect(expr({ a: 1 })).assertEqual(false);
    })

    it('exprParser_parseOperators', 0, () => {
      let expr: Function = new Expression('a.value + 12 - (2 * 14 / 4)').parse();
      class Val {
        value: number = 0;
      }
      expect(expr({ a: { value: 1 } as Val })).assertEqual(6);
      expect(expr({ a: { value: 3 } as Val })).assertEqual(8);

      expr = new Expression('a && b || c && ( d || e )').parse();
      expect(expr({ a: true, b: false, c: true, d: false, e: true })).assertEqual(true);
      expect(expr({ a: false, b: true, c: false, d: true, e: false })).assertEqual(false);

      expr = new Expression('a === b && a !== c').parse();
      expect(expr({ a: 1, b: 1, c: '1' })).assertEqual(true);
      expect(expr({ a: 1, b: 1, c: 1 })).assertEqual(false);

      expr = new Expression('a > 3 && b < 10').parse();
      expect(expr({ a: 4, b: 5 })).assertEqual(true);
      expect(expr({ a: 3, b: 5 })).assertEqual(false);
      expect(expr({ a: 4, b: 11 })).assertEqual(false);

      expr = new Expression('a.list[i + 1]').parse();
      class List {
        list: number[] = [];
      }
      expect(expr({ a: { list: [0, 5, 10] } as List, i: 1 })).assertEqual(10);
      expect(expr({ a: { list: [0, 5, 10] } as List, i: 0 })).assertEqual(5);

      expr = new Expression('a > b ? b : a').parse();
      expect(expr({ a: 2, b: 1 })).assertEqual(1);
      expect(expr({ a: 2, b: 3 })).assertEqual(2);

      expr = new Expression('a % b == 1 && c % d == 2 && e % f != 1').parse();
      expect(expr({ a: 21, b: 2, c: 8, d: 3, e: 3, f: 3 })).assertEqual(true);

      expr = new Expression('a == b').parse();
      expect(expr({ a: 10, b: 10 })).assertEqual(true);
      expect(expr({ a: 10, b: '10' })).assertEqual(true);
      expect(expr({ a: 10, b: '110' })).assertEqual(false);

      expr = new Expression('a != b').parse();
      expect(expr({ a: 10, b: 10 })).assertEqual(false);
      expect(expr({ a: 10, b: '10' })).assertEqual(false);
      expect(expr({ a: 10, b: '110' })).assertEqual(true);

      expr = new Expression('a >= b && c <= d').parse();
      expect(expr({ a: 2, b: 2, c: 3, d: 3 })).assertEqual(true);
      expect(expr({ a: 3, b: 2, c: 3, d: 4 })).assertEqual(true);
      expect(expr({ a: 2, b: 2, c: 3, d: 2 })).assertEqual(false);
      expect(expr({ a: 1, b: 2, c: 3, d: 3 })).assertEqual(false);

      expr = new Expression('!a').parse();
      expect(expr({ a: 1 })).assertEqual(false);
      expect(expr({ a: 0 })).assertEqual(true);
    })

    it('exprParser_parseObjectArrayFunction', 0, () => {
      let expr: Function = new Expression('a["b"].c + a.d["e"]').parse();
      class NumC {
        c: number = 0;
      }
      class NumE {
        e: number = 0;
      }
      class NumA {
        b: NumC = { c : 0 };
        d: NumE = { e : 0 };
      }
      expect(expr({ a: { b: { c: 1 } as NumC, d: { e: 2 } as NumE } as NumA })).assertEqual(3);

      expr = new Expression('{ a: { b: { "c": null }, d: { e: 2 }, } }').parse();
      expect(expr({})).assertDeepEquals({ a: { b: { c: null }, d: { e: 2 } } });

      expr = new Expression('a(1, 2)').parse();
      expect(expr({ a: (num1: number, num2: number): number => num1 + num2 })).assertEqual(3);

      expr = new Expression('[1, 2, 3, ][2]').parse();
      expect(expr({})).assertEqual(3);
    })
    it('error', 0, () => {
      expect(getErr('')).assertEqual('invalid expression');
      expect(getErr('{ ;a: 123 }')).assertEqual('parse expression error: { ;a: 123 }');
      expect(getErr(';')).assertEqual('parse expression error: ;');
      expect(getErr('\\')).assertEqual('invalid expression: \\');
      expect(getErr('"" || "\\uzzzz"')).assertEqual('invalid expression: "" || "\\uzzzz", invalid unicode escape [\\uzzzz]');
      expect(getErr('"')).assertEqual('invalid expression: "');
      expect(getErr('2e-a')).assertEqual('invalid expression: 2e-a');
      expect(getErr('1 === 1 ? true')).assertEqual('parse expression error: 1 === 1 ? true');
      expect(getErr('1 === 1 ? true ;')).assertEqual('parse expression error: 1 === 1 ? true ;');
    })
  })
}

function getErr(str: string): string {
  let catchErr: string = '';
  try {
    new Expression(str).parse();
  } catch (err) {
    catchErr = err.message;
  }
  return catchErr;
}