function hasControlCharacter(value: string): boolean {
  for (let index = 0; index < value.length; index += 1) {
    const code = value.charCodeAt(index);
    if (code <= 0x1f || code === 0x7f) return true;
  }
  return false;
}
const WINDOWS_DRIVE_PREFIX = /^[a-z]:/i;

export type ProjectFilePath = string & { readonly __projectFilePath: unique symbol };
export type ProjectDirectoryPath = string & {
  readonly __projectDirectoryPath: unique symbol;
};

export type ProjectPathError =
  | 'empty'
  | 'absolute'
  | 'backslash'
  | 'control-character'
  | 'dot-segment'
  | 'empty-segment'
  | 'directory-as-file';

export type ProjectPathResult<T> =
  | { ok: true; path: T }
  | { ok: false; reason: ProjectPathError };

function validateRelativePath(input: string): ProjectPathResult<string> {
  const value = input;
  if (!value) {
    return { ok: false, reason: 'empty' };
  }
  if (value.startsWith('/') || WINDOWS_DRIVE_PREFIX.test(value)) {
    return { ok: false, reason: 'absolute' };
  }
  if (value.includes('\\')) {
    return { ok: false, reason: 'backslash' };
  }
  if (hasControlCharacter(value)) {
    return { ok: false, reason: 'control-character' };
  }

  const segments = value.split('/');
  for (const segment of segments) {
    if (segment === '.' || segment === '..') {
      return { ok: false, reason: 'dot-segment' };
    }
    if (!segment) {
      return { ok: false, reason: 'empty-segment' };
    }
  }

  return { ok: true, path: value };
}

export function safeProjectFilePath(input: string): ProjectPathResult<ProjectFilePath> {
  const validated = validateRelativePath(input);
  if (!validated.ok) {
    return validated;
  }
  if (validated.path.endsWith('/')) {
    return { ok: false, reason: 'directory-as-file' };
  }
  return { ok: true, path: validated.path as ProjectFilePath };
}

export function projectFilePath(input: string): ProjectFilePath {
  const result = safeProjectFilePath(input);
  if (!result.ok) {
    throw new Error(`Invalid project file path (${result.reason}): ${input}`);
  }
  return result.path;
}

export function safeProjectDirectoryPath(
  input: string,
): ProjectPathResult<ProjectDirectoryPath> {
  const withoutTrailingSlash = input.endsWith('/') ? input.slice(0, -1) : input;
  const validated = validateRelativePath(withoutTrailingSlash);
  if (!validated.ok) {
    return validated;
  }
  return { ok: true, path: `${validated.path}/` as ProjectDirectoryPath };
}

export function projectDirectoryPath(input: string): ProjectDirectoryPath {
  const result = safeProjectDirectoryPath(input);
  if (!result.ok) {
    throw new Error(`Invalid project directory path (${result.reason}): ${input}`);
  }
  return result.path;
}

export function projectParentDirectory(
  path: ProjectFilePath,
): ProjectDirectoryPath | undefined {
  const slash = path.lastIndexOf('/');
  return slash < 0 ? undefined : projectDirectoryPath(path.slice(0, slash));
}

export function projectBaseName(path: ProjectFilePath): string {
  return path.slice(path.lastIndexOf('/') + 1);
}
