// ООП: Прототипы и ES6 классы
// задание 1 Store Class
{
  class Store {
    #reducer;
    #state;
    #cbs = [];

    constructor(reducer, state) {
      this.#reducer = reducer;
      this.#state = state;
    }

    get state() {
      return this.#state;
    }

    dispatch(action) {
      this.#state = this.#reducer(this.#state, action);
      this.#cbs.forEach((cb) => cb());
    }

    subscribe(cb) {
      this.#cbs.push(cb);
      return () => (this.#cbs = this.#cbs.filter((c) => c !== cb));
    }
  }

  const reducer = (state, action) => {
    // обработка action и возврат нового состояния
    return newState;
  };

  const store = new Store(reducer, {});

  store.subscribe(() => {
    console.log('Изменено состояние:', store.state);
  });

  store.dispatch({ type: 'INCREMENT' }); // Изменено состояние: { count: 1 }
  store.dispatch({ type: 'DECREMENT' }); // Изменено состояние: { count: 0 }
}

// задание 2 Password Class
{
  class Password {
    #input;
    #checkbox;

    constructor(parent, open = false) {
      this.#input = document.createElement('input');
      this.#input.type = open ? 'text' : 'password';
      this.#input.addEventListener('input', () => this.onChange(this.#input.value));

      this.#checkbox = document.createElement('input');
      this.#checkbox.type = 'checkbox';
      this.#checkbox.checked = open;
      this.#checkbox.addEventListener('change', () => this.setOpen(this.#checkbox.checked));

      parent.append(this.#input, this.#checkbox);
    }

    setValue(value) {
      this.#input.value = value;
    }

    getValue() {
      return this.#input.value;
    }

    setOpen(open) {
      this.#input.type = open ? 'text' : 'password';
      this.#checkbox.checked = open;
      this.onOpenChange(open);
    }

    getOpen() {
      return this.#checkbox.checked;
    }
  }
}

// задание 3 StoreThunk Class ???
{
  class StoreThunk extends Store {
    constructor(reducer) {
      super(reducer);
    }

    dispatch(action) {
      if (typeof action === 'function') {
        return action(this.dispatch.bind(this), this.getState.bind(this));
      }
      return super.dispatch(action);
    }
  }
}

// или (не уверен какой из вариантов правильный)

{
  class StoreThunk extends Store {
    constructor(reducer, state) {
      super(reducer, state);
    }

    dispatch(action) {
      if (typeof action === 'function') {
        action(this.dispatch.bind(this), this.getState.bind(this));
      } else {
        super.dispatch(action);
      }
    }
  }

  const storeThunk = new StoreThunk(reducer, {});

  storeThunk.subscribe(() => {
    console.log('State updated:', storeThunk.state);
  });

  storeThunk.dispatch({ type: 'INCREMENT' }); // State updated: { count: 1 }
  storeThunk.dispatch((dispatch, getState) => {
    setTimeout(() => {
      dispatch({ type: 'DECREMENT' });
    }, 1000);
  }); // State updated: { count: 0 } after 1 second
}

// задание 4 RGB Class
{
  class RGB {
    constructor(r, g, b) {
      this.#r = r;
      this.#g = g;
      this.#b = b;
    }

    get r() {
      return this.#r;
    }

    set r(value) {
      this.#r = value;
    }

    get g() {
      return this.#g;
    }

    set g(value) {
      this.#g = value;
    }

    get b() {
      return this.#b;
    }

    set b(value) {
      this.#b = value;
    }

    get rgb() {
      return `rgb(${this.#r}, ${this.#g}, ${this.#b})`;
    }

    set rgb(value) {
      const match = value.match(
        /^(rgb)?\(?([01]?\d\d?|2[0-4]\d|25[0-5])(\W+)([01]?\d\d?|2[0-4]\d|25[0-5])\W+(([01]?\d\d?|2[0-4]\d|25[0-5])\)?)$/,
      );
      if (match) {
        this.#r = match[2];
        this.#g = match[4];
        this.#b = match[6];
      }
    }

    get hex() {
      return `#${this.#r.toString(16).padStart(2, '0')}${this.#g
        .toString(16)
        .padStart(2, '0')}${this.#b.toString(16).padStart(2, '0')}`;
    }

    set hex(value) {
      const match = value.match(/^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/);
      if (match) {
        this.#r = parseInt(match[1], 16);
        this.#g = parseInt(match[2], 16);
        this.#b = parseInt(match[3], 16);
      }
    }
  }
}

// задание 5 RGBA Class
{
  class RGBA extends RGB {
    #a = 1;

    get a() {
      return this.#a;
    }

    set a(value) {
      if (value >= 0 && value <= 1) {
        this.#a = value;
      } else {
        throw new Error('Invalid value for a. Must be between 0 and 1');
      }
    }

    get hex() {
      return `#${super.hex.slice(1)}${Math.round(this.#a * 255)
        .toString(16)
        .padStart(2, '0')}`;
    }

    set hex(value) {
      if (value.length === 9) {
        super.hex = value.slice(0, 7);
        this.#a = parseInt(value.slice(7), 16) / 255;
      } else {
        super.hex = value;
        this.#a = 1;
      }
    }

    get rgba() {
      return `rgba(${this.#r}, ${this.#g}, ${this.#b}, ${this.#a})`;
    }

    set rgba(value) {
      const match = value.match(
        /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*([0-9]*\.[0-9]+|[0-9]+)\)$/,
      );
      if (match) {
        super.rgb = `rgb(${match[1]}, ${match[2]}, ${match[3]})`;
        this.#a = parseFloat(match[4]);
      }
    }

    set color(value) {
      if (value.startsWith('#')) {
        this.hex = value;
      } else if (value.startsWith('rgb')) {
        super.rgb = value;
        this.#a = 1;
      } else if (value.startsWith('rgba')) {
        this.rgba = value;
      }
    }
  }
}
{
  const rgba = new RGBA(0, 0, 0, 0);
  rgba.hex = '#80808080';
  console.log(rgba.a); // 0.5
  console.log(rgba.rgba); // rgba(128,128,128,0.5)
  rgba.r = 192;
  rgba.a = 0.25;
  console.log(rgba.hex); // #C0808040

  rgba.color = 'rgba(1,2,3,0.70)';
  rgba.b *= 10;
  console.log(rgba.hex); // #01021EB3
}
