from typing import List


def make_upsert_query(table_name: str, fields: List[str], key_column: str, rows_count: int) -> str:
    insert_part = _format_insert_command(table_name, fields, rows_count)
    on_conflict_part = _format_on_conflict_update(key_column, fields)
    query = f'{insert_part} {on_conflict_part}'
    return query


def _format_insert_command(table_name: str, fields: List[str], rows_count: int) -> str:
    assert rows_count > 0
    args_template = ','.join(('%s', ) * len(fields))  # result looks like '%s,%s,%s,%s'
    row_args_template = f'({args_template})'
    all_rows_template = ','.join((row_args_template, ) * rows_count)  # result looks like (%s,%s,%s),(%s,%s,%s)
    fields_string = ','.join(fields)
    insert_query = f'INSERT INTO {table_name} ({fields_string}) VALUES {all_rows_template}'
    return insert_query


def _format_on_conflict_update(key_column: str, fields: List[str]) -> str:
    field_expressions = [f'{field} = EXCLUDED.{field}' for field in fields]
    all_fields_expr = ', '.join(field_expressions)
    conflict_query = f'ON CONFLICT ({key_column}) DO UPDATE SET {all_fields_expr}'
    return conflict_query
