/**
 * Signature Value Object
 *
 * ECDSA signature.
 */

export class Signature {
  private readonly _r: Buffer;
  private readonly _s: Buffer;
  private readonly _v?: number; // Recovery parameter (optional)

  private constructor(r: Buffer, s: Buffer, v?: number) {
    this._r = r;
    this._s = s;
    this._v = v;
  }

  get r(): Buffer {
    return Buffer.from(this._r);
  }

  get s(): Buffer {
    return Buffer.from(this._s);
  }

  get v(): number | undefined {
    return this._v;
  }

  static create(r: Buffer, s: Buffer, v?: number): Signature {
    if (!r || r.length !== 32) {
      throw new Error('r must be 32 bytes');
    }
    if (!s || s.length !== 32) {
      throw new Error('s must be 32 bytes');
    }
    if (v !== undefined && (v < 0 || v > 1)) {
      throw new Error('v must be 0 or 1');
    }
    return new Signature(r, s, v);
  }

  static fromHex(hex: string): Signature {
    const buffer = Buffer.from(hex.replace('0x', ''), 'hex');
    if (buffer.length === 64) {
      return new Signature(buffer.subarray(0, 32), buffer.subarray(32, 64));
    } else if (buffer.length === 65) {
      return new Signature(
        buffer.subarray(0, 32),
        buffer.subarray(32, 64),
        buffer[64],
      );
    }
    throw new Error('Invalid signature length');
  }

  toHex(): string {
    if (this._v !== undefined) {
      return Buffer.concat([this._r, this._s, Buffer.from([this._v])]).toString('hex');
    }
    return Buffer.concat([this._r, this._s]).toString('hex');
  }

  /**
   * Convert to DER format
   */
  toDER(): Buffer {
    const rLen = this._r[0] >= 0x80 ? 33 : 32;
    const sLen = this._s[0] >= 0x80 ? 33 : 32;
    const totalLen = rLen + sLen + 4;

    const der = Buffer.alloc(2 + totalLen);
    let offset = 0;

    der[offset++] = 0x30; // SEQUENCE
    der[offset++] = totalLen;
    der[offset++] = 0x02; // INTEGER (r)
    der[offset++] = rLen;
    if (rLen === 33) der[offset++] = 0x00;
    this._r.copy(der, offset);
    offset += 32;
    der[offset++] = 0x02; // INTEGER (s)
    der[offset++] = sLen;
    if (sLen === 33) der[offset++] = 0x00;
    this._s.copy(der, offset);

    return der;
  }

  equals(other: Signature): boolean {
    return this._r.equals(other._r) && this._s.equals(other._s) && this._v === other._v;
  }

  toString(): string {
    return this.toHex();
  }
}
