class ParsedVersion {
  final int major;
  final int minor;
  final int patch;
  final String? prerelease;
  final String? build;

  const ParsedVersion({
    required this.major,
    required this.minor,
    required this.patch,
    this.prerelease,
    this.build,
  });

  bool get hasPrerelease => prerelease != null && prerelease!.isNotEmpty;

  bool sameCore(ParsedVersion other) {
    return major == other.major && minor == other.minor && patch == other.patch;
  }
}

final RegExp _versionPattern = RegExp(
  r'^[vV]?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$',
);

ParsedVersion? parseVersion(String tag) {
  final match = _versionPattern.firstMatch(tag.trim());
  if (match == null) return null;

  return ParsedVersion(
    major: int.parse(match.group(1)!),
    minor: int.parse(match.group(2)!),
    patch: int.parse(match.group(3)!),
    prerelease: match.group(4),
    build: match.group(5),
  );
}

bool isNewerVersion(String localTag, String remoteTag) {
  final local = parseVersion(localTag);
  final remote = parseVersion(remoteTag);
  if (local == null || remote == null) return false;

  if (remote.hasPrerelease) {
    if (!local.hasPrerelease || !local.sameCore(remote)) return false;
    return _comparePrerelease(remote.prerelease!, local.prerelease!) > 0;
  }

  final coreCompare = _compareCore(remote, local);
  if (coreCompare != 0) return coreCompare > 0;
  return local.hasPrerelease;
}

int _compareCore(ParsedVersion a, ParsedVersion b) {
  final major = a.major.compareTo(b.major);
  if (major != 0) return major;
  final minor = a.minor.compareTo(b.minor);
  if (minor != 0) return minor;
  return a.patch.compareTo(b.patch);
}

int _comparePrerelease(String a, String b) {
  final left = a.split('.');
  final right = b.split('.');
  final length = left.length > right.length ? left.length : right.length;

  for (var i = 0; i < length; i++) {
    if (i >= left.length) return -1;
    if (i >= right.length) return 1;

    final leftPart = left[i];
    final rightPart = right[i];
    final leftNumber = int.tryParse(leftPart);
    final rightNumber = int.tryParse(rightPart);

    if (leftNumber != null && rightNumber != null) {
      final numberCompare = leftNumber.compareTo(rightNumber);
      if (numberCompare != 0) return numberCompare;
      continue;
    }

    if (leftNumber != null) return -1;
    if (rightNumber != null) return 1;

    final textCompare = leftPart.compareTo(rightPart);
    if (textCompare != 0) return textCompare;
  }

  return 0;
}
